in fs/spiffs/src/spiffs_core.c [2795:3306]
int spiffs_fobj_truncate(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t new_size,
bool remove_full)
{
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
uint32_t cur_size;
int16_t objndx_pgndx;
int16_t data_spndx;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int16_t data_pgndx;
int16_t new_objhdr_pgndx;
int ret = OK;
/* If the file has zero (or undefined) length and we were not asked to
* remove the file, then there is nothing to do.
*/
if ((fobj->size == SPIFFS_UNDEFINED_LEN || fobj->size == 0) &&
!remove_full)
{
/* Do nothing */
return ret;
}
/* Need 2 pages if not removing: object index page + possibly chopped data
* page
*/
if (!remove_full)
{
ret = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2);
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;
objndx_pgndx = fobj->objhdr_pgndx;
cur_objndx_spndx = 0;
prev_objndx_spndx = (int16_t)-1;
data_spndx = 0;
if (fobj->size > 0)
{
data_spndx = (fobj->size - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
}
cur_size = 0;
if (fobj->size != (uint32_t)SPIFFS_UNDEFINED_LEN)
{
cur_size = fobj->size;
}
/* Before truncating, check if object is to be fully removed and mark
* this
*/
if (remove_full && new_size == 0)
{
uint8_t flags = ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX |
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_NDXDELE);
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, fobj->objhdr_pgndx) +
offsetof(struct spiffs_page_header_s, flags),
sizeof(uint8_t), (FAR uint8_t *)&flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
/* Delete from end of object until desired len is reached */
while (cur_size > new_size)
{
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
/* Put object index for current data span index in work buffer */
if (prev_objndx_spndx != cur_objndx_spndx)
{
if (prev_objndx_spndx != (int16_t)- 1)
{
/* Remove previous object index page */
finfo("Delete objndx page %04x:%04x\n",
objndx_pgndx, prev_objndx_spndx);
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx,
prev_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n",
ret);
return ret;
}
ret = spiffs_page_delete(fs, objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, NULL, SPIFFS_EV_NDXDEL, fobj->objid,
objndx->phdr.spndx, objndx_pgndx, 0);
if (prev_objndx_spndx > 0)
{
/* Update object index header page, unless we totally want
* to remove the file.
*
* If fully removing, we're not keeping consistency as
* good as when storing the header between chunks,
* would we be aborted. But when removing full files, a
* crammed system may otherwise report ERR_FULL a la
* windows. We cannot have that. Hence, take the risk -
* if aborted, a file check would free the lost pages and
* mend things as the file is marked as fully deleted in
* the beginning.
*/
if (!remove_full)
{
finfo("Update objndx hdr page %04x:%04x to"
"size=%" PRIu32 "\n",
fobj->objhdr_pgndx, prev_objndx_spndx, cur_size);
ret = spiffs_fobj_update_ndxhdr(fs, fobj,
fobj->objid,
fobj->objhdr_pgndx,
0, 0, cur_size,
&new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: "
"%d\n",
ret);
return ret;
}
}
fobj->size = cur_size;
}
}
/* Load current object index (header) page */
if (cur_objndx_spndx == 0)
{
objndx_pgndx = fobj->objhdr_pgndx;
}
else
{
ret = spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n",
ret);
return ret;
}
}
finfo("Load objndx page %04x:%04x for data spndx=%04x\n",
objndx_pgndx, cur_objndx_spndx, data_spndx);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, 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;
}
fobj->objndx_pgndx = objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = cur_size;
prev_objndx_spndx = cur_objndx_spndx;
}
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
SPIFFS_OBJID_FREE;
}
else
{
/* Get data page from object index page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = SPIFFS_OBJID_FREE;
}
finfo("Got data pgndx %04x\n", data_pgndx);
if (new_size == 0 || remove_full ||
cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs))
{
/* Delete full data page */
ret = spiffs_page_data_check(fs, fobj, data_pgndx, data_spndx);
if (ret != SPIFFS_ERR_DELETED && ret < 0 &&
ret != SPIFFS_ERR_INDEX_REF_FREE)
{
ferr("ERROR: Failed to validate data pgndx=%d\n", ret);
break;
}
if (ret >= 0)
{
ret = spiffs_page_delete(fs, data_pgndx);
if (ret < 0)
{
ferr("ERROR: Failed to delete data pgndx=%d\n", ret);
break;
}
}
else if (ret == SPIFFS_ERR_DELETED ||
ret == SPIFFS_ERR_INDEX_REF_FREE)
{
ret = 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);
}
fobj->size = cur_size;
fobj->offset = cur_size;
finfo("Delete data page %04x for data spndx=%04x,"
"cur_size=%" PRIu32 "\n",
data_pgndx, data_spndx, cur_size);
}
else
{
struct spiffs_page_header_s phdr;
int16_t new_data_pgndx;
uint32_t bytes_to_remove;
/* Delete last page, partially */
bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) -
(new_size % SPIFFS_DATA_PAGE_SIZE(fs));
finfo("Delete %" PRIu32 " bytes from data page=%04x for "
"data spndx=%04x, cur_size=%" PRIu32 "\n",
bytes_to_remove, data_pgndx, data_spndx, cur_size);
ret = spiffs_page_data_check(fs, fobj, data_pgndx, data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
break;
}
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff;
/* Allocate new page and copy unmodified data */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 0,
&new_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", ret);
break;
}
ret = spiffs_phys_cpy(fs, 0,
SPIFFS_PAGE_TO_PADDR(fs, new_data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove);
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
/* Delete original data page */
ret = spiffs_page_delete(fs, data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() 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, new_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;
}
/* 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] =
new_data_pgndx;
finfo("Wrote page=%04x to objhdr entry %04x in mem\n",
new_data_pgndx,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, 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)] = new_data_pgndx;
finfo("Wrote page %04x to objndx entry=%04x in mem\n",
new_data_pgndx,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
cur_size = new_size;
fobj->size = new_size;
fobj->offset = cur_size;
break;
}
data_spndx--;
}
/* Update object indices */
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
if (cur_size == 0)
{
if (remove_full)
{
/* Femove object altogether */
finfo("Femove object index header page=%04x\n", objndx_pgndx);
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n",
ret);
return ret;
}
ret = spiffs_page_delete(fs, objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, NULL, SPIFFS_EV_NDXDEL, fobj->objid,
0, objndx_pgndx, 0);
}
else
{
/* Make uninitialized object */
finfo("Reset objhdr page=%04x\n", objndx_pgndx);
memset(fs->work + sizeof(struct spiffs_pgobj_ndxheader_s),
0xff,
SPIFFS_GEO_PAGE_SIZE(fs) -
sizeof(struct spiffs_pgobj_ndxheader_s));
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
objndx_pgndx, fs->work,
0, SPIFFS_UNDEFINED_LEN,
&new_objhdr_pgndx);
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
}
else
{
/* Update object index header page */
finfo("Update object index header page with indices and size\n");
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
objndx_pgndx, fs->work, 0,
cur_size, &new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
}
else
{
int16_t new_objndx_pgndx;
/* Update both current object index page and object index header page */
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret);
return ret;
}
/* Move and update object index page */
ret = spiffs_page_move(fs, fobj->objid, (FAR uint8_t *)objhdr,
fobj->objid, 0, objndx_pgndx,
&new_objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)objhdr,
SPIFFS_EV_NDXUPD, fobj->objid, objndx->phdr.spndx,
new_objndx_pgndx, 0);
finfo("Store modified objndx page, %04x:%04x\n",
new_objndx_pgndx, cur_objndx_spndx);
fobj->objndx_pgndx = new_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = cur_size;
/* Update object index header page with new size */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
cur_size, &new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n", ret);
return ret;
}
}
fobj->size = cur_size;
return ret;
}