Skip to content

Using spiffs

Leo Bottaro edited this page Apr 18, 2019 · 22 revisions

Spiffs' API is posix-like, but not fully posix compliant. That said, there's still a lot to be learned from resources and examples on posix file handling on the internet. I will not try to educate in posix here. This is more of a 'Getting started' page. In these examples, it is assumed that spiffs is successfully configured (see Configure spiffs).

FIRST OF ALL

Checking errors

All api functions returns something positive (or zero) when everything is ok and something negative when something is bad.

To get the specific error code for the last failure, SPIFFS_errno can be called.

The error codes are defined in spiffs.h.

Do check the result from each spiffs call. Really. Not doing so, and considering happy-flow only, will lure out problems that are hard to detect and even might corrupt your fs. I know, it's dead boring, it makes the code dead ugly, it's a pain in the a--e.

But it's worth it.

Mounting

Mounting spiffs is equal to configuring the run-time parts. One simply calls SPIFFS_mount with correct parameters and hopes that the return code is ok. See corresponding mount function in spiffs.h for details.

Mounting can behave differently depending on if the compile-time configuration SPIFFS_USE_MAGIC is enabled or not. If enabled, this configuration will put a magic number somewhere in each block in order to validate that the underlying flash structure is indeed a spiffs file system.

If SPIFFS_USE_MAGIC is disabled, spiffs simply assumes that the underlying flash is prepared and fine, and will return ok without even actual looking at the flash. Due to the memory restriction, everything is lazily checked in spiffs.

However, if SPIFFS_USE_MAGIC is enabled, three things can happen.

  1. You'll get error SPIFFS_ERR_MAGIC_NOT_POSSIBLE. This means that with your current configuration, there simply is no space in the blocks to put the magic in. This is positive in a way, because you have managed to configure spiffs to waste no bits at all. To overcome this one can try another page size or block size.

  2. You'll get error SPIFFS_ERR_NOT_A_FS. This means that the underlying structure was checked, but spiffs found more than one block not having the magic. Spiffs allows one block without magic, would there be a power loss during an erase. Call SPIFFS_format and try mounting again.

  3. You'll get ok. Yay.

Formatting

First of all: spiffs must be unmounted when formatting flash.

Unfortunately, the formatting in spiffs became a bit awkward. The problem is that spiffs must be unmounted prior to formatting, but must be run-time configured for SPIFFS_format. Because I'm stupid, I never made a SPIFFS_config in the beginning. So you will have to mount, unmount, format, and mount again. Reasons? As stated before, I'm stupid, and because there was no formatting in the beginning. One just called a mass erase of the flash. Well, read on.

Formatting is dependent on if the compile-time configuration SPIFFS_USE_MAGIC is enabled or not.

If SPIFFS_USE_MAGIC is disabled, spiffs expects a fresh flash where all bits are set. You have two options:

  1. Just call a mass erase of the spi flash yourself, not involving spiffs. Then mount.

  2. Mount (to configure the run-time parts), unmount, call SPIFFS_format, and mount again (sigh).

If SPIFFS_USE_MAGIC is enabled, mounting procedure will look for magic. Recommended formatting procedure is as point two above. Recapped:

  1. Call SPIFFS_mount

  2. If SPIFFS_mount fails with SPIFFS_ERR_NOT_A_FS, keep going. Otherwise, call SPIFFS_unmount

  3. Call SPIFFS_format

  4. Call SPIFFS_mount again.

Again, in all cases: spiffs must be unmounted when formatting flash. Bad things will happen otherwise.

SIMPLE EXAMPLES

In following examples, I assume you have a fully working spiffs, configured and mounted. The spiffs struct is the variable fs.

Create a file and read it back

  char buf[12];

  // create a file, delete previous if it already exists, and open it for reading and writing
  spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
  if (fd < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }
  // write to it
  if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }
  // close it
  if (SPIFFS_close(&fs, fd) < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }

  // open it
  fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
  if (fd < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }

  // read it
  if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }
  // close it
  if (SPIFFS_close(&fs, fd) < 0) {
    printf("errno %i\n", SPIFFS_errno(&fs));
    return;
  }

  // check it
  printf("--> %s <--\n", buf);

Do I need to check result after SPIFFS_close?

Not if any of the build time configurations SPIFFS_CACHE or SPIFFS_CACHE_WR are disabled. But if both are enabled, writes are cached and might be performed when closing the file. So in this case, yes.

SPIFFS_creat function?

Why is there a SPIFFS_creat? Well, you could create files this way also. This is a posix thing. It is a lot more convenient to create and open it in the same call using SPIFFS_open. There's not many cases where one want to create a file and do nothing more.

What are the flags SPIFFS_CREAT, SPIFFS_TRUNC etc?

As for the flags to SPIFFS_open, have a look in spiffs.h, or even better, here. Just remember, all posix flags are not implemented in spiffs.

List contents

  spiffs_DIR d;
  struct spiffs_dirent e;
  struct spiffs_dirent *pe = &e;

  SPIFFS_opendir(&fs, "/", &d);
  while ((pe = SPIFFS_readdir(&d, pe))) {
    printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
  }
  SPIFFS_closedir(&d);

Find files by name and open it

As spiffs is inherently slow when it comes to opening files (as the whole system is searched until the name is found) there is a non-posix way of doing this. This is useful when doing operations on files with e.g. some prefix. One lists file by dirent (as previous example) and use the function SPIFFS_open_by_dirent.

Do note that if the any file is modified (except for fully removing) within the dirent iterator, the iterator may become invalid.

  // remove all files starting with "tmp_"
  char *search_prefix = "tmp_";
  spiffs_DIR d;
  struct spiffs_dirent e;
  struct spiffs_dirent *pe = &e;
  int res;

  spiffs_file fd = -1;

  SPIFFS_opendir(&fs, "/", &d);
  while ((pe = SPIFFS_readdir(&d, pe))) {
    if (0 == strncmp(search_prefix, (char *)pe->name, strlen(search_prefix))) {
      // found one
      fd = SPIFFS_open_by_dirent(&fs, pe, SPIFFS_RDWR, 0);
      if (fd < 0) {
        printf("errno %i\n", SPIFFS_errno(&fs));
        return;
      }
      res = SPIFFS_fremove(&fs, fd);
      if (res < 0) {
        printf("errno %i\n", SPIFFS_errno(&fs));
        return;
      }
      res = SPIFFS_close(&fs, fd);
      if (res < 0) {
        printf("errno %i\n", SPIFFS_errno(&fs));
        return;
      }
    }
  }
  SPIFFS_closedir(&d);

Seeking in a file

As opposed to posix, spiffs cannot create gaps in files. Sorry. You can seek around, but seeking beyond file end will promptly put the offset at the file end.

  u8_t buf[128];
  int i, res;
  u8_t test;

#define SPIFFS_CHECK(x) \
  if ((x) < 0) { printf("errno:%i\n", SPIFFS_errno(&fs)); return; }

  for (i = 0; i < 128; i++) buf[i] = i;

  // create a file with some data in it
  spiffs_file fd = SPIFFS_open(&fs, "somedata", SPIFFS_CREAT | SPIFFS_RDWR, 0);
  SPIFFS_CHECK(fd);
  res = SPIFFS_write(&fs, fd, buf, 128);
  SPIFFS_CHECK(res);
  res = SPIFFS_close(&fs, fd);
  SPIFFS_CHECK(res);

  // open for reading
  fd = SPIFFS_open(&fs, "somedata", SPIFFS_CREAT | SPIFFS_RDONLY, 0);
  SPIFFS_CHECK(fd);

  // read the last byte of the file
  res = SPIFFS_lseek(&fs, fd, -1, SPIFFS_SEEK_END);
  SPIFFS_CHECK(res);
  res = SPIFFS_read(&fs, fd, &test, 1);
  SPIFFS_CHECK(res);
  printf("last byte:%i\n", test); // prints "last byte:127"

  // read the middle byte of the file
  res = SPIFFS_lseek(&fs, fd, 64, SPIFFS_SEEK_SET);
  SPIFFS_CHECK(res);
  res = SPIFFS_read(&fs, fd, &test, 1);
  SPIFFS_CHECK(res);
  printf("middle byte:%i\n", test); // prints "middle byte:64"

  // skip 3 bytes from current offset and read next byte (NB we read one byte previously also)
  res = SPIFFS_lseek(&fs, fd, 3, SPIFFS_SEEK_CUR);
  SPIFFS_CHECK(res);
  res = SPIFFS_read(&fs, fd, &test, 1);
  SPIFFS_CHECK(res);
  printf("middle+4 byte:%i\n", test); // prints "middle+4 byte:68"

  res = SPIFFS_close(&fs, fd);
  SPIFFS_CHECK(res);

Remove file

You got to options: either by name or by file handle. Pretty self-explanatory.

  res = SPIFFS_remove(&fs, "supersecret");

or

  spiffs_file fd = SPIFFS_open(&fs, "supersecret", SPIFFS_RDWR, 0);
  .. check fd obviously..
  res = SPIFFS_fremove(&fs, fd);
  .. check res obviously..
  res = SPIFFS_close(&fs, fd);
  .. check again..

The latter will invalidate the file-handle fd but it still needs to be closed to release resources.

Checking files

To check file info, one either calls by name or by file handle:

  spiffs_stat s;
  res = SPIFFS_stat(&fs, "foo", &s);

or

  spiffs_stat s;
  res = SPIFFS_fstat(&fs, fd, &s);

The spiffs_stat struct contains name (file name) and size (file length). The struct also contains obj_id, the object id, sort of an inode number. type is not used for now.

Rename files

Simply call:

  res = SPIFFS_rename(&fs, "oldfoo", "newfoo");

Flushing files

If any of build time configurations SPIFFS_CACHE or SPIFFS_CACHE_WR are disabled, this is a no op. Otherwise flushing will store any writes being currently cached to flash.

  res = SPIFFS_fflush(&fs, fd);

Checking filesystem

To get an indication of how much spare room there is on the flash, call

  u32_t total, used;
  res = SPIFFS_info(&fs, &total, &used);

As usual, the total amount will be less than what you've given spiffs in terms of flash area, as metadata takes it toll. Also, using free = total - used to get free amount of bytes should be taken with a grain of salt. If you plan to create on file filling all the free data, it is probably pretty correct. If you plan to divide up this free space amongst many files it is not very correct, as each new file needs metadata apart from the actual data.

Also, getting used > total is an indication of there was a power loss in midst of things. In this case one should run (the never-ending function) SPIFFS_check(&fs) to try to mend things.

Mapping file index to memory

TODO