-
Notifications
You must be signed in to change notification settings - Fork 402
Using spiffs
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).
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 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.
-
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. -
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. CallSPIFFS_format
and try mounting again. -
You'll get ok. Yay.
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:
-
Just call a mass erase of the spi flash yourself, not involving spiffs. Then mount.
-
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:
-
Call
SPIFFS_mount
-
If
SPIFFS_mount
fails withSPIFFS_ERR_NOT_A_FS
, keep going. Otherwise, callSPIFFS_unmount
-
Call
SPIFFS_format
-
Call
SPIFFS_mount
again.
Again, in all cases: spiffs must be unmounted when formatting flash. Bad things will happen otherwise.
In following examples, I assume you have a fully working spiffs, configured and mounted. The spiffs
struct is the variable fs
.
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);
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.
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.
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.
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);
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);
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);
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.
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.
Simply call:
res = SPIFFS_rename(&fs, "oldfoo", "newfoo");
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);
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.
TODO