Beispiel #1
0
u32_t tfile_get_size(tfile_size s) {
  switch (s) {
  case EMPTY:
    return 0;
  case SMALL: // half a data page
    return SPIFFS_DATA_PAGE_SIZE(FS)/2;
  case MEDIUM: // one block
    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
  case LARGE: // third of fs
    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)) * (FS)->block_count/3;
  }
  return 0;
}
Beispiel #2
0
uint32_t tfile_get_size(tfile_size s) {
  switch (s) {
  case EMPTY:
    return 0;
  case SMALL:
    return SPIFFS_DATA_PAGE_SIZE(FS)/2;
  case MEDIUM:
    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
  case LARGE:
    return (FS)->cfg.phys_size/3;
  }
  return 0;
}
Beispiel #3
0
} TEST_END

#if !SPIFFS_READ_ONLY
TEST(truncate_48) {
  fs_reset();

  u32_t len = SPIFFS_DATA_PAGE_SIZE(FS)/2;

  s32_t res = test_create_and_write_file("small", len, len);
  TEST_CHECK_GE(res, 0);

  spiffs_file fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0);
  TEST_CHECK_GE(fd, 0);

  spiffs_fd *desc;
#if SPIFFS_FILEHDL_OFFSET
  res = spiffs_fd_get(FS, fd - TEST_SPIFFS_FILEHDL_OFFSET, &desc);
#else
  res = spiffs_fd_get(FS, fd, &desc);
#endif

  TEST_CHECK_GE(res, 0);

  TEST_CHECK_EQ(desc->size, len);

  u32_t new_len = len/2;
  res = spiffs_object_truncate(desc, new_len, 0);
  TEST_CHECK_GE(res, 0);

  TEST_CHECK_EQ(desc->size, new_len);

  res = SPIFFS_close(FS, fd);
  TEST_CHECK_GE(res, 0);

  spiffs_stat s;
  res = SPIFFS_stat(FS, "small", &s);
  TEST_CHECK_GE(res, 0);
  TEST_CHECK_EQ(s.size, new_len);

  res = SPIFFS_remove(FS, "small");
  TEST_CHECK_GE(res, 0);

  fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0);
  TEST_CHECK_LT(fd, 0);
  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_FOUND);

  return TEST_RES_OK;
} TEST_END
Beispiel #4
0
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
  SPIFFS_API_CHECK_CFG(fs);
  SPIFFS_API_CHECK_MOUNT(fs);
  SPIFFS_LOCK(fs);

  spiffs_fd *fd;
  s32_t res;
  fh = SPIFFS_FH_UNOFFS(fs, fh);
  res = spiffs_fd_get(fs, fh, &fd);
  SPIFFS_API_CHECK_RES(fs, res);

#if SPIFFS_CACHE_WR
  spiffs_fflush_cache(fs, fh);
#endif

  switch (whence) {
  case SPIFFS_SEEK_CUR:
    offs = fd->fdoffset+offs;
    break;
  case SPIFFS_SEEK_END:
    offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs;
    break;
  }

  if ((offs > (s32_t)fd->size) && (SPIFFS_UNDEFINED_LEN != fd->size)) {
    res = SPIFFS_ERR_END_OF_OBJECT;
  }
  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);

  spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs);
  spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
  if (fd->cursor_objix_spix != objix_spix) {
    spiffs_page_ix pix;
    res = spiffs_obj_lu_find_id_and_span(
        fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix);
    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
    fd->cursor_objix_spix = objix_spix;
    fd->cursor_objix_pix = pix;
  }
  fd->fdoffset = offs;

  SPIFFS_UNLOCK(fs);

  return offs;
}
Beispiel #5
0
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
  s32_t res = SPIFFS_OK;
  SPIFFS_API_CHECK_CFG(fs);
  SPIFFS_API_CHECK_MOUNT(fs);
  SPIFFS_LOCK(fs);

  u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
  u32_t blocks = fs->block_count;
  u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
  u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs);
  u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page

  if (total) {
    *total = total_data_pages * data_page_size;
  }

  if (used) {
    *used = fs->stats_p_allocated * data_page_size;
  }

  SPIFFS_UNLOCK(fs);
  return res;
}
Beispiel #6
0
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) {
  int res;
  tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files);
  memset(tfiles, 0, sizeof(tfile) * max_concurrent_files);
  int run = 0;
  int cur_config_ix = 0;
  char name[32];
  while (run < max_runs)  {
    if (dbg) printf(" run %i/%i\n", run, max_runs);
    int i;
    for (i = 0; i < max_concurrent_files; i++) {
      sprintf(name, "file%i_%i", (1+run), i);
      tfile *tf = &tfiles[i];
      if (tf->state == 0 && cur_config_ix < cfg_count) {
// create a new file
        strcpy(tf->name, name);
        tf->state = 1;
        tf->cfg = cfgs[cur_config_ix];
        int size = tfile_get_size(tf->cfg.tsize);
        if (dbg) printf("   create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size);

        if (tf->cfg.tsize == EMPTY) {
          res = SPIFFS_creat(FS, name, 0);
          CHECK_RES(res);
          int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
          close(pfd);
          int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
          spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0);
          CHECK(fd > 0);
          tf->fd = fd;
        } else {
          int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
          spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
          CHECK(fd > 0);
          extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0;
          int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
          tf->fd = fd;
          uint8_t *buf = malloc(size);
          memrand(buf, size);
          res = SPIFFS_write(FS, fd, buf, size);
          CHECK_RES(res);
          write(pfd, buf, size);
          close(pfd);
          free(buf);
          res = read_and_verify(name);
          CHECK_RES(res);
        }

        cur_config_ix++;
      } else if (tf->state > 0) {
// hande file lifecycle
        switch (tf->cfg.ttype) {
        case UNTAMPERED: {
          break;
        }
        case APPENDED: {
          if (dbg) printf("   appending %s\n", tf->name);
          int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
          uint8_t *buf = malloc(size);
          memrand(buf, size);
          res = SPIFFS_write(FS, tf->fd, buf, size);
          CHECK_RES(res);
          int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR);
          write(pfd, buf, size);
          close(pfd);
          free(buf);
          res = read_and_verify(tf->name);
          CHECK_RES(res);
          break;
        }
        case MODIFIED: {
          if (dbg) printf("   modify %s\n", tf->name);
          spiffs_stat stat;
          res = SPIFFS_fstat(FS, tf->fd, &stat);
          CHECK_RES(res);
          int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3;
          int offs = (stat.size / tf->cfg.tlife) * tf->state;
          res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET);
          CHECK_RES(res);
          uint8_t *buf = malloc(size);
          memrand(buf, size);
          res = SPIFFS_write(FS, tf->fd, buf, size);
          CHECK_RES(res);
          int pfd = open(make_test_fname(tf->name), O_RDWR);
          lseek(pfd, offs, SEEK_SET);
          write(pfd, buf, size);
          close(pfd);
          free(buf);
          res = read_and_verify(tf->name);
          CHECK_RES(res);
          break;
        }
        case REWRITTEN: {
          if (tf->fd > 0) {
            SPIFFS_close(FS, tf->fd);
          }
          if (dbg) printf("   rewriting %s\n", tf->name);
          spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
          CHECK(fd > 0);
          int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
          tf->fd = fd;
          int size = tfile_get_size(tf->cfg.tsize);
          uint8_t *buf = malloc(size);
          memrand(buf, size);
          res = SPIFFS_write(FS, fd, buf, size);
          CHECK_RES(res);
          write(pfd, buf, size);
          close(pfd);
          free(buf);
          res = read_and_verify(tf->name);
          CHECK_RES(res);
          break;
        }
        }
        tf->state++;
        if (tf->state > tf->cfg.tlife) {
// file outlived its time, kill it
          if (tf->fd > 0) {
            SPIFFS_close(FS, tf->fd);
          }
          if (dbg) printf("   removing %s\n", tf->name);
          res = read_and_verify(tf->name);
          CHECK_RES(res);
          res = SPIFFS_remove(FS, tf->name);
          CHECK_RES(res);
          remove(make_test_fname(tf->name));
          memset(tf, 0, sizeof(tf));
        }

      }
    }

    run++;
  }
  free(tfiles);
  return 0;
}
Beispiel #7
0
// Checks if garbage collecting is necessary. If so a candidate block is found,
// cleansed and erased
s32_t spiffs_gc_check(
    spiffs *fs,
    u32_t len) {
  s32_t res;
  s32_t free_pages =
      (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
      - fs->stats_p_allocated - fs->stats_p_deleted;
  int tries = 0;

  if (fs->free_blocks > 3 &&
      (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
    return SPIFFS_OK;
  }

  u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
//  if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
//    SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
//    return SPIFFS_ERR_FULL;
//  }
  if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
    SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
    return SPIFFS_ERR_FULL;
  }

  do {
    SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
        tries,
        fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
        len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));

    spiffs_block_ix *cands;
    int count;
    spiffs_block_ix cand;
    s32_t prev_free_pages = free_pages;
    // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
    res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
    SPIFFS_CHECK_RES(res);
    if (count == 0) {
      SPIFFS_GC_DBG("gc_check: no candidates, return\n");
      return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
    }
#if SPIFFS_GC_STATS
    fs->stats_gc_runs++;
#endif
    cand = cands[0];
    fs->cleaning = 1;
    //printf("gcing: cleaning block %i\n", cand);
    res = spiffs_gc_clean(fs, cand);
    fs->cleaning = 0;
    if (res < 0) {
      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
    } else {
      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
    }
    SPIFFS_CHECK_RES(res);

    res = spiffs_gc_erase_page_stats(fs, cand);
    SPIFFS_CHECK_RES(res);

    res = spiffs_gc_erase_block(fs, cand);
    SPIFFS_CHECK_RES(res);

    free_pages =
          (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
          - fs->stats_p_allocated - fs->stats_p_deleted;

    if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
      // abort early to reduce wear, at least tried once
      SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
      break;
    }

  } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
      (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));

  free_pages =
        (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
        - fs->stats_p_allocated - fs->stats_p_deleted;
  if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
    res = SPIFFS_ERR_FULL;
  }

  SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
      fs->stats_p_allocated + fs->stats_p_deleted,
      fs->free_blocks, free_pages, tries, res);

  return res;
}
Beispiel #8
0
// Checks if garbaga collecting is necessary. If so a candidate block is found,
// cleansed and erased
s32_t spiffs_gc_check(
    spiffs *fs,
    u32_t len) {
  s32_t res;
  s32_t free_pages =
      (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
      - fs->stats_p_allocated - fs->stats_p_deleted;
  int tries = 0;

  if (fs->free_blocks > 3 &&
      (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
    return SPIFFS_OK;
  }

  u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
  if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
    return SPIFFS_ERR_FULL;
  }

  //printf("gcing started  %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len);

  do {
    SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
        tries,
        fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
        len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));

    spiffs_block_ix *cands;
    int count;
    spiffs_block_ix cand;
    res = spiffs_gc_find_candidate(fs, &cands, &count);
    SPIFFS_CHECK_RES(res);
    if (count == 0) {
      SPIFFS_GC_DBG("gc_check: no candidates, return\n");
      return res;
    }
#if SPIFFS_GC_STATS
    fs->stats_gc_runs++;
#endif
    cand = cands[0];
    fs->cleaning = 1;
    //printf("gcing: cleaning block %i\n", cand);
    res = spiffs_gc_clean(fs, cand);
    fs->cleaning = 0;
    if (res < 0) {
      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
    } else {
      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
    }
    SPIFFS_CHECK_RES(res);

    res = spiffs_gc_erase_page_stats(fs, cand);
    SPIFFS_CHECK_RES(res);

    res = spiffs_gc_erase_block(fs, cand);
    SPIFFS_CHECK_RES(res);

    free_pages =
          (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
          - fs->stats_p_allocated - fs->stats_p_deleted;

  } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
      (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));

  free_pages =
        (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
        - fs->stats_p_allocated - fs->stats_p_deleted;
  if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
    res = SPIFFS_ERR_FULL;
  }

  SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
      fs->stats_p_allocated + fs->stats_p_deleted,
      fs->free_blocks, free_pages, tries, res);

  return res;
}