in fs/spiffs/src/spiffs_core.c [2294:2743]
ssize_t spiffs_fobj_modify(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;
size_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_pgndx;
int16_t data_spndx;
int16_t data_pgndx;
int ret = OK;
int ret2;
ret = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_gc_check() 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;
int16_t orig_data_pgndx;
/* 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 (header) page, unless first
* pass.
*/
if (prev_objndx_spndx == 0)
{
/* Store previous object index header page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx,
fs->work, 0, 0,
&new_objhdr_pgndx);
finfo("Store modified objhdr page, "
"%04x:%04x, nwritten=%zu\n",
new_objhdr_pgndx, 0, nwritten);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
else
{
/* Store new version of previous object index page */
int16_t new_objndx_pgndx;
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_page_move(fs, fobj->objid,
(FAR uint8_t *)objndx,
fobj->objid, 0, cur_objndx_pgndx,
&new_objndx_pgndx);
finfo("Store previous modified objndx page, %04x:%04x, "
"nwritten=%zu\n",
new_objndx_pgndx, objndx->phdr.spndx, nwritten);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
objndx,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, new_objndx_pgndx,
0);
}
}
/* Load next object index page */
if (cur_objndx_spndx == 0)
{
/* Load object index header page, must exist */
finfo("Load objndxhdr page %04x:%04x\n",
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 pgndx;
/* Load existing object index page on first pass */
finfo("Find objndx spndx=%04x\n", 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("Found object index at page=%04x\n", pgndx);
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;
prev_objndx_spndx = cur_objndx_spndx;
}
/* Write partial data */
to_write = MIN(len - nwritten, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
orig_data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
}
else
{
/* Get data page from object index page */
orig_data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
}
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff;
if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs))
{
/* A full page, allocate and write a new page of data */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, &data[nwritten], to_write,
page_offs, 1, &data_pgndx);
finfo("Store new data page, %04x:%04x offset=%" PRId32 ", "
"len=%zu, nwritten=%zu\n",
data_pgndx, data_spndx, page_offs, to_write, nwritten);
}
else
{
/* Write to existing page, allocate new and copy unmodified data */
ret = spiffs_page_data_check(fs, fobj, orig_data_pgndx,
data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
return ret;
}
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 0, &data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", ret);
break;
}
/* Copy unmodified data */
if (page_offs > 0)
{
/* Before modification */
ret = spiffs_phys_cpy(fs, fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_PAGE_TO_PADDR(fs,
orig_data_pgndx) +
sizeof(struct spiffs_page_header_s),
page_offs);
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
}
if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs))
{
/* After modification */
ret =
spiffs_phys_cpy(fs, fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
page_offs +
to_write,
SPIFFS_PAGE_TO_PADDR(fs, orig_data_pgndx)
+ sizeof(struct spiffs_page_header_s) +
page_offs +
to_write,
SPIFFS_DATA_PAGE_SIZE(fs) -
(page_offs + to_write));
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
}
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
page_offs,
to_write, &data[nwritten]);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
break;
}
phdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
offsetof(struct spiffs_page_header_s,
flags),
sizeof(uint8_t),
(FAR uint8_t *)&phdr.flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
break;
}
finfo("Store to existing data page, src=%04x, dest=%04x:%04x "
"offset=%" PRId32 ", len=%zu, nwritten=%zu\n",
orig_data_pgndx, data_pgndx, data_spndx, page_offs,
to_write, nwritten);
}
/* Delete original data page */
ret = spiffs_page_delete(fs, orig_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
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_pgndx;
finfo("Wrote page %04x to objhdr entry=%04x in mem\n",
data_pgndx, data_spndx);
}
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_pgndx;
finfo("Wrote page %04x to objndx entry %04x in mem\n",
data_pgndx, (int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
/* Update internals */
page_offs = 0;
data_spndx++;
nwritten += to_write;
}
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)
{
int16_t new_objndx_pgndx;
/* Wrote beyond object index header page. Write last modified object
* index page. Move and update page
*/
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_page_move(fs, fobj->objid, (uint8_t *) objndx,
fobj->objid, 0, cur_objndx_pgndx,
&new_objndx_pgndx);
finfo("Store modified objndx page, %04x:%04x, nwritten=%zu\n",
new_objndx_pgndx, cur_objndx_spndx, nwritten);
fobj->objndx_pgndx = new_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)objndx,
SPIFFS_EV_NDXUPD, fobj->objid, objndx->phdr.spndx,
new_objndx_pgndx, 0);
}
else
{
/* Wrote within object index header page */
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, fs->work, 0, 0,
&new_objhdr_pgndx);
finfo("Store modified objhdr page, %04x:%04x, nwritten=%zu\n",
new_objhdr_pgndx, 0, nwritten);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
return nwritten;
}