int spiffs_fobj_truncate()

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;
}