ssize_t spiffs_fobj_modify()

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