mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	This adds a fully independent layer between the rest of the filesystem and the block device. This requires some additionally logic around cache invalidation and flushing, but removes the need for any higher layer to consider read/write sizes less than what is supported by the hardware. Additionally, these caches can be used for possible speed improvements. This is left up to the user to optimize for their use cases. For very limited embedded systems with byte-level read/writes, the caches could be omitted completely, or they could even be the size of a full block for minimizing storage access. (A full block may not be the best for speed, consider if only a small portion of the read block is used, but I'll leave that evaluation as an exercise for any consumers of this library)
		
			
				
	
	
		
			266 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * The little filesystem
 | |
|  *
 | |
|  * Copyright (c) 2017 Christopher Haster
 | |
|  * Distributed under the MIT license
 | |
|  */
 | |
| #ifndef LFS_H
 | |
| #define LFS_H
 | |
| 
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| 
 | |
| 
 | |
| // Type definitions
 | |
| typedef uint32_t lfs_size_t;
 | |
| typedef uint32_t lfs_off_t;
 | |
| 
 | |
| typedef int32_t  lfs_ssize_t;
 | |
| typedef int32_t  lfs_soff_t;
 | |
| 
 | |
| typedef uint32_t lfs_block_t;
 | |
| 
 | |
| 
 | |
| // Configurable littlefs constants
 | |
| #ifndef LFS_NAME_MAX
 | |
| #define LFS_NAME_MAX 255
 | |
| #endif
 | |
| 
 | |
| // The littefs constants
 | |
| enum lfs_error {
 | |
|     LFS_ERR_OK      = 0,
 | |
|     LFS_ERR_IO      = -5,
 | |
|     LFS_ERR_CORRUPT = -77,
 | |
|     LFS_ERR_NOENT   = -2,
 | |
|     LFS_ERR_EXISTS  = -17,
 | |
|     LFS_ERR_NOTDIR  = -20,
 | |
|     LFS_ERR_ISDIR   = -21,
 | |
|     LFS_ERR_INVAL   = -22,
 | |
|     LFS_ERR_NOSPC   = -28,
 | |
|     LFS_ERR_NOMEM   = -12,
 | |
| };
 | |
| 
 | |
| enum lfs_type {
 | |
|     LFS_TYPE_REG        = 0x01,
 | |
|     LFS_TYPE_DIR        = 0x02,
 | |
|     LFS_TYPE_SUPERBLOCK = 0x10,
 | |
| };
 | |
| 
 | |
| enum lfs_open_flags {
 | |
|     // open flags
 | |
|     LFS_O_RDONLY = 1,
 | |
|     LFS_O_WRONLY = 2,
 | |
|     LFS_O_RDWR   = 3,
 | |
|     LFS_O_CREAT  = 0x0100,
 | |
|     LFS_O_EXCL   = 0x0200,
 | |
|     LFS_O_TRUNC  = 0x0400,
 | |
|     LFS_O_APPEND = 0x0800,
 | |
| 
 | |
|     // internally used flags
 | |
|     LFS_F_DIRTY   = 0x10000,
 | |
|     LFS_F_WRITING = 0x20000,
 | |
|     LFS_F_READING = 0x40000,
 | |
| };
 | |
| 
 | |
| enum lfs_whence_flags {
 | |
|     LFS_SEEK_SET = 0,
 | |
|     LFS_SEEK_CUR = 1,
 | |
|     LFS_SEEK_END = 2,
 | |
| };
 | |
| 
 | |
| 
 | |
| // Configuration provided during initialization of the littlefs
 | |
| struct lfs_config {
 | |
|     // Opaque user provided context
 | |
|     void *context;
 | |
| 
 | |
|     // Read a region in a block
 | |
|     int (*read)(const struct lfs_config *c, lfs_block_t block,
 | |
|             lfs_off_t off, void *buffer, lfs_size_t size);
 | |
| 
 | |
|     // Program a region in a block. The block must have previously
 | |
|     // been erased.
 | |
|     int (*prog)(const struct lfs_config *c, lfs_block_t block,
 | |
|             lfs_off_t off, const void *buffer, lfs_size_t size);
 | |
| 
 | |
|     // Erase a block. A block must be erased before being programmed.
 | |
|     // The state of an erased block is undefined.
 | |
|     int (*erase)(const struct lfs_config *c, lfs_block_t block);
 | |
| 
 | |
|     // Sync the state of the underlying block device
 | |
|     int (*sync)(const struct lfs_config *c);
 | |
| 
 | |
|     // Minimum size of a read. This may be larger than the physical
 | |
|     // read size to cache reads from the block device.
 | |
|     lfs_size_t read_size;
 | |
| 
 | |
|     // Minimum size of a program. This may be larger than the physical
 | |
|     // program size to cache programs to the block device.
 | |
|     lfs_size_t prog_size;
 | |
| 
 | |
|     // Size of an erasable block.
 | |
|     lfs_size_t block_size;
 | |
| 
 | |
|     // Number of erasable blocks on the device.
 | |
|     lfs_size_t block_count;
 | |
| 
 | |
|     // Number of blocks to lookahead during block allocation.
 | |
|     lfs_size_t lookahead;
 | |
| 
 | |
|     // Optional, statically allocated read buffer. Must be read sized.
 | |
|     void *read_buffer;
 | |
| 
 | |
|     // Optional, statically allocated program buffer. Must be program sized.
 | |
|     void *prog_buffer;
 | |
| 
 | |
|     // Optional, statically allocated lookahead buffer.
 | |
|     // Must be 1 bit per lookahead block.
 | |
|     void *lookahead_buffer;
 | |
| 
 | |
|     // Optional, statically allocated buffer for files. Must be program sized.
 | |
|     // If enabled, only one file may be opened at a time
 | |
|     void *file_buffer;
 | |
| };
 | |
| 
 | |
| // File info structure
 | |
| struct lfs_info {
 | |
|     // Type of the file, either REG or DIR
 | |
|     uint8_t type;
 | |
| 
 | |
|     // Size of the file, only valid for REG files
 | |
|     lfs_size_t size;
 | |
| 
 | |
|     // Name of the file stored as a null-terminated string
 | |
|     char name[LFS_NAME_MAX+1];
 | |
| };
 | |
| 
 | |
| 
 | |
| // littlefs data structures
 | |
| typedef struct lfs_entry {
 | |
|     lfs_off_t off;
 | |
| 
 | |
|     struct lfs_disk_entry {
 | |
|         uint16_t type;
 | |
|         uint16_t len;
 | |
|         union {
 | |
|             struct {
 | |
|                 lfs_block_t head;
 | |
|                 lfs_size_t size;
 | |
|             } file;
 | |
|             lfs_block_t dir[2];
 | |
|         } u;
 | |
|     } d;
 | |
| } lfs_entry_t;
 | |
| 
 | |
| typedef struct lfs_cache {
 | |
|     lfs_block_t block;
 | |
|     lfs_off_t off;
 | |
|     uint8_t *buffer;
 | |
| } lfs_cache_t;
 | |
| 
 | |
| typedef struct lfs_file {
 | |
|     struct lfs_file *next;
 | |
|     lfs_block_t pair[2];
 | |
|     lfs_off_t poff;
 | |
| 
 | |
|     lfs_block_t head;
 | |
|     lfs_size_t size;
 | |
| 
 | |
|     uint32_t flags;
 | |
|     lfs_off_t pos;
 | |
|     lfs_block_t block;
 | |
|     lfs_off_t off;
 | |
|     lfs_cache_t cache;
 | |
| } lfs_file_t;
 | |
| 
 | |
| typedef struct lfs_dir {
 | |
|     lfs_block_t pair[2];
 | |
|     lfs_off_t off;
 | |
| 
 | |
|     lfs_block_t head[2];
 | |
|     lfs_off_t pos;
 | |
| 
 | |
|     struct lfs_disk_dir {
 | |
|         uint32_t rev;
 | |
|         lfs_size_t size;
 | |
|         lfs_block_t tail[2];
 | |
|     } d;
 | |
| } lfs_dir_t;
 | |
| 
 | |
| typedef struct lfs_superblock {
 | |
|     lfs_off_t off;
 | |
| 
 | |
|     struct lfs_disk_superblock {
 | |
|         uint16_t type;
 | |
|         uint16_t len;
 | |
|         uint32_t version;
 | |
|         char magic[8];
 | |
|         uint32_t block_size;
 | |
|         uint32_t block_count;
 | |
|         lfs_block_t root[2];
 | |
|     } d;
 | |
| } lfs_superblock_t;
 | |
| 
 | |
| typedef struct lfs_free {
 | |
|     lfs_block_t start;
 | |
|     lfs_block_t off;
 | |
|     uint32_t *lookahead;
 | |
| } lfs_free_t;
 | |
| 
 | |
| // littlefs type
 | |
| typedef struct lfs {
 | |
|     const struct lfs_config *cfg;
 | |
|     lfs_size_t words;       // number of 32-bit words that can fit in a block
 | |
| 
 | |
|     lfs_block_t root[2];
 | |
|     lfs_dir_t *scratch;
 | |
|     lfs_file_t *files;
 | |
| 
 | |
|     lfs_cache_t rcache;
 | |
|     lfs_cache_t pcache;
 | |
| 
 | |
|     lfs_free_t free;
 | |
| } lfs_t;
 | |
| 
 | |
| 
 | |
| // filesystem functions
 | |
| int lfs_format(lfs_t *lfs, const struct lfs_config *config);
 | |
| int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
 | |
| int lfs_unmount(lfs_t *lfs);
 | |
| 
 | |
| // general operations
 | |
| int lfs_remove(lfs_t *lfs, const char *path);
 | |
| int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
 | |
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
 | |
| 
 | |
| // directory operations
 | |
| int lfs_mkdir(lfs_t *lfs, const char *path);
 | |
| int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
 | |
| int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
 | |
| int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
 | |
| int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
 | |
| lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
 | |
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
 | |
| 
 | |
| // file operations
 | |
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
 | |
|         const char *path, int flags);
 | |
| int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
 | |
| int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
 | |
| lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
 | |
|         void *buffer, lfs_size_t size);
 | |
| lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
 | |
|         const void *buffer, lfs_size_t size);
 | |
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
 | |
|         lfs_soff_t off, int whence);
 | |
| lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
 | |
| int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
 | |
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
 | |
| 
 | |
| // miscellaneous lfs specific operations
 | |
| int lfs_deorphan(lfs_t *lfs);
 | |
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
 | |
| 
 | |
| 
 | |
| #endif
 |