LittleFileSystem
LittleFileSystem class hierarchy
The little file system (LittleFS) is a fail-safe file system designed for embedded systems, specifically for microcontrollers that use external flash storage.
| | | .---._____
.-----. | |
--|o |---| LittleFS |
--| |---| |
'-----' '----------'
| | |
Microcontrollers and flash storage present three challenges for embedded storage: power loss, wear and limited RAM and ROM. This file system provides a solution to all three of these problems.
-
Bounded RAM/ROM - This file system works with a limited amount of memory. It avoids recursion and limits dynamic memory to configurable buffers that can be provided statically.
-
Power-loss resilient - We have designed this for operating systems that may have random power failures. It has strong copy-on-write guarantees and keeps storage on disk in a valid state.
-
Wear leveling - Because the most common form of embedded storage is erodible flash memories, the file system provides a form of dynamic wear leveling for systems that cannot fit a full flash translation layer.
For additional information, please see the storage overview page.
Use cases
We built this for microcontrollers with external flash storage. In this context, it outperforms the other Mbed OS file systems in terms of RAM, ROM, wear and runtime.
For storage on an SD card that is accessible from a PC, use the FATFileSystem due to its portability.
LittleFileSystem is thread safe.
Usage
Instantiate the LittleFileSystem
class with a block device and file path.
The API that this presents is the standard Mbed OS file system API. Once declared, Mbed OS provides the retargeting layer for the standard C library.
To configure this class, please see the FileSystem configuration documentation.
LittleFileSystem class reference
Public Member Functions | |
LittleFileSystem (const char *name=NULL, mbed::BlockDevice *bd=NULL, lfs_size_t read_size=MBED_LFS_READ_SIZE, lfs_size_t prog_size=MBED_LFS_PROG_SIZE, lfs_size_t block_size=MBED_LFS_BLOCK_SIZE, lfs_size_t lookahead=MBED_LFS_LOOKAHEAD) | |
Lifetime of the LittleFileSystem. More... | |
virtual int | mount (mbed::BlockDevice *bd) |
Mount a file system to a block device. More... | |
virtual int | unmount () |
Unmount a file system from the underlying block device. More... | |
virtual int | reformat (mbed::BlockDevice *bd) |
Reformat a file system. More... | |
virtual int | remove (const char *path) |
Remove a file from the file system. More... | |
virtual int | rename (const char *path, const char *newpath) |
Rename a file in the file system. More... | |
virtual int | stat (const char *path, struct stat *st) |
Store information about the file in a stat structure. More... | |
virtual int | mkdir (const char *path, mode_t mode) |
Create a directory in the file system. More... | |
virtual int | statvfs (const char *path, struct statvfs *buf) |
Store information about the mounted file system in a statvfs structure. More... | |
FileHandle * | open (const char *path, int flags) |
Open a file on the filesystem. More... | |
DirHandle * | opendir (const char *path) |
Open a directory on the filesystem. More... |
Static Public Member Functions | |
static int | format (mbed::BlockDevice *bd, lfs_size_t read_size=MBED_LFS_READ_SIZE, lfs_size_t prog_size=MBED_LFS_PROG_SIZE, lfs_size_t block_size=MBED_LFS_BLOCK_SIZE, lfs_size_t lookahead=MBED_LFS_LOOKAHEAD) |
Format a block device with the LittleFileSystem. More... | |
static FileSystem * | get_default_instance () |
Return the default file system. More... |
Protected Member Functions | |
virtual int | open (FileHandle **file, const char *path, int flags) |
defined(DOXYGEN_ONLY) More... | |
virtual int | open (DirHandle **dir, const char *path) |
Open a directory on the filesystem. More... |
LittleFileSystem example
/* mbed Microcontroller Library
* Copyright (c) 2006-2019 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include <stdio.h>
#include <errno.h>
#include "BlockDevice.h"
// Maximum number of elements in buffer
#define BUFFER_MAX_LEN 10
#define FORCE_REFORMAT true
// This will take the system's default block device
BlockDevice *bd = BlockDevice::get_default_instance();
// Instead of the default block device, you can define your own block device.
// For example: HeapBlockDevice with size of 2048 bytes, read size 1, write size 1 and erase size 512.
// #include "HeapBlockDevice.h"
// BlockDevice *bd = new HeapBlockDevice(2048, 1, 1, 512);
// This example uses LittleFileSystem as the default file system
#include "LittleFileSystem.h"
LittleFileSystem fs("fs");
// Uncomment the following two lines and comment the previous two to use FAT file system.
// #include "FATFileSystem.h"
// FATFileSystem fs("fs");
// Set up the button to trigger an erase
InterruptIn irq(BUTTON1);
void erase() {
printf("Initializing the block device... ");
fflush(stdout);
int err = bd->init();
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
printf("Erasing the block device... ");
fflush(stdout);
err = bd->erase(0, bd->size());
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
printf("Deinitializing the block device... ");
fflush(stdout);
err = bd->deinit();
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
}
// Entry point for the example
int main() {
printf("--- Mbed OS filesystem example ---\n");
// Setup the erase event on button press, use the event queue
// to avoid running in interrupt context
irq.fall(mbed_event_queue()->event(erase));
// Try to mount the filesystem
printf("Mounting the filesystem... ");
fflush(stdout);
int err = fs.mount(bd);
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err || FORCE_REFORMAT) {
// Reformat if we can't mount the filesystem
printf("formatting... ");
fflush(stdout);
err = fs.reformat(bd);
printf("%s\n", (err ? "Fail :(" : "OK"));
if (err) {
error("error: %s (%d)\n", strerror(-err), err);
}
}
// Open the numbers file
printf("Opening \"/fs/numbers.txt\"... ");
fflush(stdout);
FILE *f = fopen("/fs/numbers.txt", "r+");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
// Create the numbers file if it doesn't exist
printf("No file found, creating a new file... ");
fflush(stdout);
f = fopen("/fs/numbers.txt", "w+");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
for (int i = 0; i < 10; i++) {
printf("\rWriting numbers (%d/%d)... ", i, 10);
fflush(stdout);
err = fprintf(f, " %d\n", i);
if (err < 0) {
printf("Fail :(\n");
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
printf("\rWriting numbers (%d/%d)... OK\n", 10, 10);
printf("Seeking file... ");
fflush(stdout);
err = fseek(f, 0, SEEK_SET);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
}
// Go through and increment the numbers
for (int i = 0; i < 10; i++) {
printf("\rIncrementing numbers (%d/%d)... ", i, 10);
fflush(stdout);
// Get current stream position
long pos = ftell(f);
// Parse out the number and increment
char buf[BUFFER_MAX_LEN];
if (!fgets(buf, BUFFER_MAX_LEN, f)) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
char *endptr;
int32_t number = strtol(buf, &endptr, 10);
if (
(errno == ERANGE) || // The number is too small/large
(endptr == buf) || // No character was read
(*endptr && *endptr != '\n') // The whole input was not converted
) {
continue;
}
number += 1;
// Seek to beginning of number
fseek(f, pos, SEEK_SET);
// Store number
fprintf(f, " %d\n", number);
// Flush between write and read on same file
fflush(f);
}
printf("\rIncrementing numbers (%d/%d)... OK\n", 10, 10);
// Close the file which also flushes any cached writes
printf("Closing \"/fs/numbers.txt\"... ");
fflush(stdout);
err = fclose(f);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
// Display the root directory
printf("Opening the root directory... ");
fflush(stdout);
DIR *d = opendir("/fs/");
printf("%s\n", (!d ? "Fail :(" : "OK"));
if (!d) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
printf("root directory:\n");
while (true) {
struct dirent *e = readdir(d);
if (!e) {
break;
}
printf(" %s\n", e->d_name);
}
printf("Closing the root directory... ");
fflush(stdout);
err = closedir(d);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
// Display the numbers file
printf("Opening \"/fs/numbers.txt\"... ");
fflush(stdout);
f = fopen("/fs/numbers.txt", "r");
printf("%s\n", (!f ? "Fail :(" : "OK"));
if (!f) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
printf("numbers:\n");
while (!feof(f)) {
int c = fgetc(f);
printf("%c", c);
}
printf("\rClosing \"/fs/numbers.txt\"... ");
fflush(stdout);
err = fclose(f);
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(errno), -errno);
}
// Tidy up
printf("Unmounting... ");
fflush(stdout);
err = fs.unmount();
printf("%s\n", (err < 0 ? "Fail :(" : "OK"));
if (err < 0) {
error("error: %s (%d)\n", strerror(-err), err);
}
printf("Mbed OS filesystem example done!\n");
}