ssize_t spiffs_fobj_append()

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