mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-12-01 12:20:02 +00:00
Mostly for consistency with mtrv.b and gbmap.b, but also (1) this hopefully reduces confusion around the fact that these can refer to both bshrubs and btrees, and (2) saves a bit of typing with the messy struct namespaces forced by C's strict aliasing.
1487 lines
47 KiB
C
1487 lines
47 KiB
C
/*
|
|
* The little filesystem
|
|
*
|
|
* Copyright (c) 2022, The littlefs authors.
|
|
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#ifndef LFS3_H
|
|
#define LFS3_H
|
|
|
|
#include "lfs3_util.h"
|
|
|
|
|
|
/// Version info ///
|
|
|
|
// Software library version
|
|
// Major (top-nibble), incremented on backwards incompatible changes
|
|
// Minor (bottom-nibble), incremented on feature additions
|
|
#define LFS3_VERSION 0x00000000
|
|
#define LFS3_VERSION_MAJOR (0xffff & (LFS3_VERSION >> 16))
|
|
#define LFS3_VERSION_MINOR (0xffff & (LFS3_VERSION >> 0))
|
|
|
|
// Version of On-disk data structures
|
|
// Major (top-nibble), incremented on backwards incompatible changes
|
|
// Minor (bottom-nibble), incremented on feature additions
|
|
#define LFS3_DISK_VERSION 0x00000000
|
|
#define LFS3_DISK_VERSION_MAJOR (0xffff & (LFS3_DISK_VERSION >> 16))
|
|
#define LFS3_DISK_VERSION_MINOR (0xffff & (LFS3_DISK_VERSION >> 0))
|
|
|
|
|
|
/// Definitions ///
|
|
|
|
// Type definitions
|
|
typedef uint32_t lfs3_size_t;
|
|
typedef int32_t lfs3_ssize_t;
|
|
|
|
typedef uint32_t lfs3_off_t;
|
|
typedef int32_t lfs3_soff_t;
|
|
|
|
typedef uint32_t lfs3_block_t;
|
|
typedef int32_t lfs3_sblock_t;
|
|
|
|
typedef uint32_t lfs3_rid_t;
|
|
typedef int32_t lfs3_srid_t;
|
|
|
|
typedef uint16_t lfs3_tag_t;
|
|
typedef int16_t lfs3_stag_t;
|
|
|
|
typedef uint32_t lfs3_bid_t;
|
|
typedef int32_t lfs3_sbid_t;
|
|
|
|
typedef uint32_t lfs3_mid_t;
|
|
typedef int32_t lfs3_smid_t;
|
|
|
|
typedef uint32_t lfs3_did_t;
|
|
typedef int32_t lfs3_sdid_t;
|
|
|
|
// Maximum name size in bytes, may be redefined to reduce the size of the
|
|
// info struct. Limited to <= 1022. Stored in superblock and must be
|
|
// respected by other littlefs drivers.
|
|
#ifndef LFS3_NAME_MAX
|
|
#define LFS3_NAME_MAX 255
|
|
#endif
|
|
|
|
// Maximum size of a file in bytes, may be redefined to limit to support other
|
|
// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
|
|
// respected by other littlefs drivers.
|
|
#ifndef LFS3_FILE_MAX
|
|
#define LFS3_FILE_MAX 2147483647
|
|
#endif
|
|
|
|
|
|
// Possible error codes, these are negative to allow
|
|
// valid positive return values
|
|
enum lfs3_err {
|
|
LFS3_ERR_OK = 0, // No error
|
|
LFS3_ERR_UNKNOWN = -1, // Unknown error
|
|
LFS3_ERR_INVAL = -22, // Invalid parameter
|
|
LFS3_ERR_NOTSUP = -95, // Operation not supported
|
|
LFS3_ERR_IO = -5, // Error during device operation
|
|
LFS3_ERR_CORRUPT = -84, // Corrupted
|
|
LFS3_ERR_NOENT = -2, // No directory entry
|
|
LFS3_ERR_EXIST = -17, // Entry already exists
|
|
LFS3_ERR_NOTDIR = -20, // Entry is not a dir
|
|
LFS3_ERR_ISDIR = -21, // Entry is a dir
|
|
LFS3_ERR_NOTEMPTY = -39, // Dir is not empty
|
|
LFS3_ERR_FBIG = -27, // File too large
|
|
LFS3_ERR_NOSPC = -28, // No space left on device
|
|
LFS3_ERR_NOMEM = -12, // No more memory available
|
|
LFS3_ERR_NOATTR = -61, // No data/attr available
|
|
LFS3_ERR_NAMETOOLONG = -36, // File name too long
|
|
LFS3_ERR_RANGE = -34, // Result out of range
|
|
};
|
|
|
|
// File types
|
|
//
|
|
// LFS3_TYPE_UNKNOWN will always be the largest, including internal
|
|
// types, and can be used to deliminate user defined types at higher
|
|
// levels
|
|
//
|
|
enum lfs3_type {
|
|
// file types
|
|
LFS3_TYPE_REG = 1, // A regular file
|
|
LFS3_TYPE_DIR = 2, // A directory file
|
|
LFS3_TYPE_STICKYNOTE = 3, // An uncommitted file
|
|
LFS3_TYPE_UNKNOWN = 7, // Unknown file type
|
|
|
|
// internally used types, don't use these
|
|
LFS3_type_BOOKMARK = 4, // Directory bookmark
|
|
LFS3_type_ORPHAN = 5, // An orphaned stickynote
|
|
LFS3_type_TRV = 6, // An open traversal object
|
|
};
|
|
|
|
// File open flags
|
|
#define LFS3_O_MODE 3 // The file's access mode
|
|
#define LFS3_O_RDONLY 0 // Open a file as read only
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_WRONLY 1 // Open a file as write only
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_RDWR 2 // Open a file as read and write
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_CREAT 0x00000004 // Create a file if it does not exist
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_EXCL 0x00000008 // Fail if a file already exists
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_TRUNC 0x00000010 // Truncate the existing file to zero size
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_O_APPEND 0x00000020 // Move to end of file on every write
|
|
#endif
|
|
#define LFS3_O_FLUSH 0x00000040 // Flush data on every write
|
|
#define LFS3_O_SYNC 0x00000080 // Sync metadata on every write
|
|
#define LFS3_O_DESYNC 0x04000000 // Do not sync or recieve file updates
|
|
#define LFS3_O_CKMETA 0x00001000 // Check metadata checksums
|
|
#define LFS3_O_CKDATA 0x00002000 // Check metadata + data checksums
|
|
|
|
// internally used flags, don't use these
|
|
#define LFS3_o_WRSET 3 // Open a file as an atomic write
|
|
#define LFS3_o_TYPE 0xf0000000 // The file's type
|
|
#define LFS3_o_ZOMBIE 0x08000000 // File has been removed
|
|
#define LFS3_o_UNCREAT 0x02000000 // File does not exist yet
|
|
#define LFS3_o_UNSYNC 0x01000000 // File's metadata does not match disk
|
|
#define LFS3_o_UNCRYST 0x00800000 // File's leaf not fully crystallized
|
|
#define LFS3_o_UNGRAFT 0x00400000 // File's leaf does not match disk
|
|
#define LFS3_o_UNFLUSH 0x00200000 // File's cache does not match disk
|
|
|
|
// File seek flags
|
|
#define LFS3_SEEK_SET 0 // Seek relative to an absolute position
|
|
#define LFS3_SEEK_CUR 1 // Seek relative to the current file position
|
|
#define LFS3_SEEK_END 2 // Seek relative to the end of the file
|
|
|
|
// Custom attribute flags
|
|
#define LFS3_A_MODE 3 // The attr's access mode
|
|
#define LFS3_A_RDONLY 0 // Open an attr as read only
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_A_WRONLY 1 // Open an attr as write only
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_A_RDWR 2 // Open an attr as read and write
|
|
#endif
|
|
#define LFS3_A_LAZY 0x04 // Only write attr if file changed
|
|
|
|
// Filesystem format flags
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_F_MODE 1 // Format's access mode
|
|
#define LFS3_F_RDWR 0 // Format the filesystem as read and write
|
|
#ifdef LFS3_REVDBG
|
|
#define LFS3_F_REVDBG 0x00000010 // Add debug info to revision counts
|
|
#endif
|
|
#ifdef LFS3_REVNOISE
|
|
#define LFS3_F_REVNOISE 0x00000020 // Add noise to revision counts
|
|
#endif
|
|
#ifdef LFS3_CKPROGS
|
|
#define LFS3_F_CKPROGS 0x00080000 // Check progs by reading back progged data
|
|
#endif
|
|
#ifdef LFS3_CKFETCHES
|
|
#define LFS3_F_CKFETCHES \
|
|
0x00100000 // Check block checksums before first use
|
|
#endif
|
|
#ifdef LFS3_CKMETAPARITY
|
|
#define LFS3_F_CKMETAPARITY \
|
|
0x00200000 // Check metadata tag parity bits
|
|
#endif
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
#define LFS3_F_CKDATACKSUMS \
|
|
0x00800000 // Check data checksums on reads
|
|
#endif
|
|
|
|
#define LFS3_F_CKMETA 0x00001000 // Check metadata checksums
|
|
#define LFS3_F_CKDATA 0x00002000 // Check metadata + data checksums
|
|
#endif
|
|
|
|
#ifdef LFS3_GBMAP
|
|
#define LFS3_F_GBMAP 0x01000000 // Use the global on-disk block-map
|
|
#endif
|
|
|
|
// Filesystem mount flags
|
|
#define LFS3_M_MODE 1 // Mount's access mode
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_M_RDWR 0 // Mount the filesystem as read and write
|
|
#endif
|
|
#define LFS3_M_RDONLY 1 // Mount the filesystem as read only
|
|
#define LFS3_M_FLUSH 0x00000040 // Open all files with LFS3_O_FLUSH
|
|
#define LFS3_M_SYNC 0x00000080 // Open all files with LFS3_O_SYNC
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_REVDBG)
|
|
#define LFS3_M_REVDBG 0x00000010 // Add debug info to revision counts
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_REVNOISE)
|
|
#define LFS3_M_REVNOISE 0x00000020 // Add noise to revision counts
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_CKPROGS)
|
|
#define LFS3_M_CKPROGS 0x00080000 // Check progs by reading back progged data
|
|
#endif
|
|
#ifdef LFS3_CKFETCHES
|
|
#define LFS3_M_CKFETCHES \
|
|
0x00100000 // Check block checksums before first use
|
|
#endif
|
|
#ifdef LFS3_CKMETAPARITY
|
|
#define LFS3_M_CKMETAPARITY \
|
|
0x00200000 // Check metadata tag parity bits
|
|
#endif
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
#define LFS3_M_CKDATACKSUMS \
|
|
0x00800000 // Check data checksums on reads
|
|
#endif
|
|
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_M_MKCONSISTENT \
|
|
0x00000100 // Make the filesystem consistent
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_M_RELOOKAHEAD \
|
|
0x00000200 // Repopulate lookahead buffer
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
|
#define LFS3_M_REGBMAP 0x00000400 // Repopulate the gbmap
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_M_COMPACTMETA \
|
|
0x00000800 // Compact metadata logs
|
|
#endif
|
|
#define LFS3_M_CKMETA 0x00001000 // Check metadata checksums
|
|
#define LFS3_M_CKDATA 0x00002000 // Check metadata + data checksums
|
|
|
|
|
|
// Filesystem info flags
|
|
#define LFS3_I_RDONLY 0x00000001 // Mounted read only
|
|
#define LFS3_I_FLUSH 0x00000040 // Mounted with LFS3_M_FLUSH
|
|
#define LFS3_I_SYNC 0x00000080 // Mounted with LFS3_M_SYNC
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_REVDBG)
|
|
#define LFS3_I_REVDBG 0x00000010 // Mounted with LFS3_M_REVDBG
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_REVNOISE)
|
|
#define LFS3_I_REVNOISE 0x00000020 // Mounted with LFS3_M_REVNOISE
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_CKPROGS)
|
|
#define LFS3_I_CKPROGS 0x00080000 // Mounted with LFS3_M_CKPROGS
|
|
#endif
|
|
#ifdef LFS3_CKFETCHES
|
|
#define LFS3_I_CKFETCHES \
|
|
0x00100000 // Mounted with LFS3_M_CKFETCHES
|
|
#endif
|
|
#ifdef LFS3_CKMETAPARITY
|
|
#define LFS3_I_CKMETAPARITY \
|
|
0x00200000 // Mounted with LFS3_M_CKMETAPARITY
|
|
#endif
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
#define LFS3_I_CKDATACKSUMS \
|
|
0x00800000 // Mounted with LFS3_M_CKDATACKSUMS
|
|
#endif
|
|
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_I_MKCONSISTENT \
|
|
0x00000100 // Filesystem needs mkconsistent to write
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_I_RELOOKAHEAD \
|
|
0x00000200 // Lookahead buffer is not full
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
|
#define LFS3_I_REGBMAP 0x00000400 // The gbmap is not full
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_I_COMPACTMETA \
|
|
0x00000800 // Filesystem may have uncompacted metadata
|
|
#endif
|
|
#define LFS3_I_CKMETA 0x00001000 // Metadata checksums not checked recently
|
|
#define LFS3_I_CKDATA 0x00002000 // Data checksums not checked recently
|
|
|
|
#ifdef LFS3_GBMAP
|
|
#define LFS3_I_GBMAP 0x01000000 // Global on-disk block-map in use
|
|
#endif
|
|
|
|
// internally used flags, don't use these
|
|
#ifdef LFS3_REVDBG
|
|
#define LFS3_i_INMODE 0x00030000 // Btree commit mode
|
|
#define LFS3_i_INMTREE 0x00010000 // Committing to mtree
|
|
#define LFS3_i_INGBMAP 0x00020000 // Committing to gbmap
|
|
#endif
|
|
|
|
|
|
// Block types
|
|
enum lfs3_btype {
|
|
LFS3_BTYPE_MDIR = 1,
|
|
LFS3_BTYPE_BTREE = 2,
|
|
LFS3_BTYPE_DATA = 3,
|
|
};
|
|
|
|
// Traversal flags
|
|
#define LFS3_T_MODE 1 // The traversal's access mode
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_T_RDWR 0 // Open traversal as read and write
|
|
#endif
|
|
#define LFS3_T_RDONLY 1 // Open traversal as read only
|
|
#define LFS3_T_MTREEONLY \
|
|
0x00000002 // Only traverse the mtree
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_T_MKCONSISTENT \
|
|
0x00000100 // Make the filesystem consistent
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_T_RELOOKAHEAD \
|
|
0x00000200 // Repopulate lookahead buffer
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
|
#define LFS3_T_REGBMAP 0x00000400 // Repopulate the gbmap
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_T_COMPACTMETA \
|
|
0x00000800 // Compact metadata logs
|
|
#endif
|
|
#define LFS3_T_CKMETA 0x00001000 // Check metadata checksums
|
|
#define LFS3_T_CKDATA 0x00002000 // Check metadata + data checksums
|
|
|
|
// internally used flags, don't use these
|
|
#define LFS3_t_TYPE 0xf0000000 // The traversal's type
|
|
#define LFS3_t_BTYPE 0x00f00000 // The current block type
|
|
#define LFS3_t_ZOMBIE 0x08000000 // File has been removed
|
|
#define LFS3_t_CKPOINTED \
|
|
0x04000000 // Filesystem ckpointed during traversal
|
|
#define LFS3_t_DIRTY 0x02000000 // Filesystem ckpointed outside traversal
|
|
#define LFS3_t_STALE 0x01000000 // Block queue probably out-of-date
|
|
|
|
// GC flags
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_GC_MKCONSISTENT \
|
|
0x00000100 // Make the filesystem consistent
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_GC_RELOOKAHEAD \
|
|
0x00000200 // Repopulate lookahead buffer
|
|
#endif
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
|
#define LFS3_GC_REGBMAP 0x00000400 // Repopulate the gbmap
|
|
#endif
|
|
#ifndef LFS3_RDONLY
|
|
#define LFS3_GC_COMPACTMETA \
|
|
0x00000800 // Compact metadata logs
|
|
#endif
|
|
#define LFS3_GC_CKMETA 0x00001000 // Check metadata checksums
|
|
#define LFS3_GC_CKDATA 0x00002000 // Check metadata + data checksums
|
|
|
|
// an alias for all possible GC work
|
|
#define LFS3_GC_ALL ( \
|
|
LFS3_IFDEF_RDONLY(0, LFS3_GC_MKCONSISTENT) \
|
|
| LFS3_IFDEF_RDONLY(0, LFS3_GC_RELOOKAHEAD) \
|
|
| LFS3_IFDEF_RDONLY(0, \
|
|
LFS3_IFDEF_GBMAP(LFS3_GC_REGBMAP, 0)) \
|
|
| LFS3_IFDEF_RDONLY(0, LFS3_GC_COMPACTMETA) \
|
|
| LFS3_GC_CKMETA \
|
|
| LFS3_GC_CKDATA)
|
|
|
|
|
|
// Configuration provided during initialization of the littlefs
|
|
struct lfs3_cfg {
|
|
// Opaque user provided context that can be used to pass
|
|
// information to the block device operations
|
|
void *context;
|
|
|
|
// Read a region in a block. Negative error codes are propagated
|
|
// to the user.
|
|
int (*read)(const struct lfs3_cfg *c, lfs3_block_t block,
|
|
lfs3_off_t off, void *buffer, lfs3_size_t size);
|
|
|
|
// Program a region in a block. The block must have previously
|
|
// been erased. Negative error codes are propagated to the user.
|
|
// May return LFS3_ERR_CORRUPT if the block should be considered bad.
|
|
#ifndef LFS3_RDONLY
|
|
int (*prog)(const struct lfs3_cfg *c, lfs3_block_t block,
|
|
lfs3_off_t off, const void *buffer, lfs3_size_t size);
|
|
#endif
|
|
|
|
// Erase a block. A block must be erased before being programmed.
|
|
// The state of an erased block is undefined. Negative error codes
|
|
// are propagated to the user.
|
|
// May return LFS3_ERR_CORRUPT if the block should be considered bad.
|
|
#ifndef LFS3_RDONLY
|
|
int (*erase)(const struct lfs3_cfg *c, lfs3_block_t block);
|
|
#endif
|
|
|
|
// Sync the state of the underlying block device. Negative error codes
|
|
// are propagated to the user.
|
|
#ifndef LFS3_RDONLY
|
|
int (*sync)(const struct lfs3_cfg *c);
|
|
#endif
|
|
|
|
#ifdef LFS3_THREADSAFE
|
|
// Lock the underlying block device. Negative error codes
|
|
// are propagated to the user.
|
|
int (*lock)(const struct lfs3_cfg *c);
|
|
|
|
// Unlock the underlying block device. Negative error codes
|
|
// are propagated to the user.
|
|
int (*unlock)(const struct lfs3_cfg *c);
|
|
#endif
|
|
|
|
// Minimum size of a read in bytes. All read operations will be a
|
|
// multiple of this value.
|
|
lfs3_size_t read_size;
|
|
|
|
// Minimum size of a program in bytes. All program operations will be a
|
|
// multiple of this value.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t prog_size;
|
|
#endif
|
|
|
|
// Size of an erasable block in bytes. This does not impact ram consumption
|
|
// and may be larger than the physical erase size. Must be a multiple of
|
|
// the read and program sizes.
|
|
lfs3_size_t block_size;
|
|
|
|
// Number of erasable blocks on the device.
|
|
lfs3_size_t block_count;
|
|
|
|
// Number of erase cycles before metadata blocks are relocated for
|
|
// wear-leveling. Suggested values are in the range 16-1024. Larger values
|
|
// relocate less frequently, improving average performance, at the cost
|
|
// of worse wear distribution. Note this ends up rounded down to a
|
|
// power-of-2.
|
|
//
|
|
// 0 results in pure copy-on-write, which may be counter-productive. Set
|
|
// to -1 to disable block-level wear-leveling.
|
|
#ifndef LFS3_RDONLY
|
|
int32_t block_recycles;
|
|
#endif
|
|
|
|
// Size of the read cache in bytes. Larger caches can improve
|
|
// performance by storing more data and reducing the number of disk
|
|
// accesses. Must be a multiple of the read size.
|
|
lfs3_size_t rcache_size;
|
|
|
|
// Size of the program cache in bytes. Larger caches can improve
|
|
// performance by storing more data and reducing the number of disk
|
|
// accesses. Must be a multiple of the program size.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t pcache_size;
|
|
#endif
|
|
|
|
// Size of file caches in bytes. In addition to filesystem-wide
|
|
// read/prog caches, each file gets its own cache to reduce disk
|
|
// accesses.
|
|
lfs3_size_t fcache_size;
|
|
|
|
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
|
// increases the number of blocks found during an allocation scan. The
|
|
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
|
// can track 8 blocks.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t lookahead_size;
|
|
#endif
|
|
|
|
// Flags indicating what gc work to do during lfs3_gc calls.
|
|
#ifdef LFS3_GC
|
|
uint32_t gc_flags;
|
|
#endif
|
|
|
|
// Number of gc steps to perform in each call to lfs3_gc, with each
|
|
// step being ~1 block of work.
|
|
//
|
|
// More steps per call will make more progress if interleaved with
|
|
// other filesystem operations, but may also introduce more latency.
|
|
// steps=1 will do the minimum amount of work to make progress, and
|
|
// steps=-1 will not return until all pending janitorial work has
|
|
// been completed.
|
|
//
|
|
// Defaults to steps=1 when zero.
|
|
#ifdef LFS3_GC
|
|
lfs3_soff_t gc_steps;
|
|
#endif
|
|
|
|
// Threshold for repopulating the lookahead buffer during gc. This
|
|
// can be set lower than the lookahead size to delay gc work when
|
|
// only a few blocks have been allocated.
|
|
//
|
|
// Note this only affects explicit gc operations. During normal
|
|
// operations the lookahead buffer is only repopulated when empty.
|
|
//
|
|
// 0 only repopulates the lookahead buffer when empty, while -1 or
|
|
// any value >= 8*lookahead_size repopulates the lookahead buffer
|
|
// after any block allocation.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_block_t gc_relookahead_thresh;
|
|
#endif
|
|
|
|
// Threshold for repopulating the gbmap during gc. This can be set
|
|
// lower than the disk size to delay gc work when only a few blocks
|
|
// have been allocated.
|
|
//
|
|
// Note this only affects explicit gc operations. During normal
|
|
// operations gbmap repopulations are controlled by
|
|
// regbmap_thresh.
|
|
//
|
|
// Any value <= regbmap_thresh repopulates the gbmap when below
|
|
// regbmap_thresh, while -1 or any value >= block_count
|
|
// repopulates the lookahead buffer after any block allocation.
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP)
|
|
lfs3_block_t gc_regbmap_thresh;
|
|
#endif
|
|
|
|
// Threshold for metadata compaction during gc in bytes.
|
|
//
|
|
// Metadata logs that exceed this threshold will be compacted during
|
|
// gc operations. Defaults to ~88% block_size when zero, though this
|
|
// default may change in the future.
|
|
//
|
|
// Note this only affects explicit gc operations. During normal
|
|
// operations metadata is only compacted when full.
|
|
//
|
|
// Set to -1 to disable metadata compaction during gc.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t gc_compactmeta_thresh;
|
|
#endif
|
|
|
|
// Optional statically allocated rcache buffer. Must be rcache_size. By
|
|
// default lfs3_malloc is used to allocate this buffer.
|
|
void *rcache_buffer;
|
|
|
|
// Optional statically allocated pcache buffer. Must be pcache_size. By
|
|
// default lfs3_malloc is used to allocate this buffer.
|
|
#ifndef LFS3_RDONLY
|
|
void *pcache_buffer;
|
|
#endif
|
|
|
|
// Optional statically allocated lookahead buffer. Must be lookahead_size.
|
|
// By default lfs3_malloc is used to allocate this buffer.
|
|
#ifndef LFS3_RDONLY
|
|
void *lookahead_buffer;
|
|
#endif
|
|
|
|
// Optional upper limit on length of file names in bytes. No downside for
|
|
// larger names except the size of the info struct which is controlled by
|
|
// the LFS3_NAME_MAX define. Defaults to LFS3_NAME_MAX when zero. Stored in
|
|
// superblock and must be respected by other littlefs drivers.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t name_limit;
|
|
#endif
|
|
|
|
// Optional upper limit on files in bytes. No downside for larger files
|
|
// but must be <= LFS3_FILE_MAX. Defaults to LFS3_FILE_MAX when zero. Stored
|
|
// in superblock and must be respected by other littlefs drivers.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t file_limit;
|
|
#endif
|
|
|
|
// TODO these are pretty low-level details, should we have reasonable
|
|
// defaults? need to benchmark.
|
|
|
|
// Maximum size of inlined trees (shrubs) in bytes. Shrubs reduce B-tree
|
|
// root overhead, but may impact metadata-related performance. Must be <=
|
|
// blocksize/4.
|
|
//
|
|
// 0 disables shrubs.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t shrub_size;
|
|
#endif
|
|
|
|
// Maximum size of a non-block B-tree leaf in bytes. Smaller values may
|
|
// make small random-writes cheaper, but increase metadata overhead. Must
|
|
// be <= block_size/4.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t fragment_size;
|
|
#endif
|
|
|
|
// TODO crystal_thresh=0 really just means crystal_thresh=1, should we
|
|
// allow crystal_thresh=0? crystal_thresh=0 => block_size/16 or
|
|
// block_size/8 is probably a better default. need to benchmark.
|
|
|
|
// TODO we should probably just assert if crystal_thresh < fragment_size,
|
|
// or if crystal_thresh < prog_size, these aren't really valid cases
|
|
|
|
// Threshold for compacting multiple fragments into a block. Smaller
|
|
// values will crystallize more eagerly, reducing disk usage, but
|
|
// increasing the cost of random-writes.
|
|
//
|
|
// 0 tries to only writes blocks, minimizing disk usage, while -1 or
|
|
// any value > block_size only writes fragments, minimizing
|
|
// random-write cost.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_size_t crystal_thresh;
|
|
#endif
|
|
|
|
// Threshold for repopulating the global on-disk block-map (gbmap).
|
|
//
|
|
// When <= this many blocks have a known state, littlefs will
|
|
// traverse the filesystem and attempt to repopulate the gbmap.
|
|
// Smaller values decrease repopulation frequency and improves
|
|
// overall allocator throughput, at the risk of needing to fallback
|
|
// to the slower lookahead allocator when empty.
|
|
//
|
|
// 0 only repopulates the gbmap when empty, minimizing gbmap
|
|
// repops at the risk of large latency spikes.
|
|
#ifdef LFS3_GBMAP
|
|
lfs3_block_t regbmap_thresh;
|
|
#endif
|
|
};
|
|
|
|
// File info structure
|
|
struct lfs3_info {
|
|
// Type of the file, either LFS3_TYPE_REG or LFS3_TYPE_DIR
|
|
uint8_t type;
|
|
|
|
// Size of the file, only valid for REG files. Limited to 32-bits.
|
|
lfs3_size_t size;
|
|
|
|
// Name of the file stored as a null-terminated string. Limited to
|
|
// LFS3_NAME_MAX+1, which can be changed by redefining LFS3_NAME_MAX to
|
|
// reduce RAM. LFS3_NAME_MAX is stored in superblock and must be
|
|
// respected by other littlefs drivers.
|
|
char name[LFS3_NAME_MAX+1];
|
|
};
|
|
|
|
// Filesystem info structure
|
|
struct lfs3_fsinfo {
|
|
// Filesystem flags
|
|
uint32_t flags;
|
|
|
|
// Size of a logical block in bytes.
|
|
lfs3_size_t block_size;
|
|
|
|
// Number of logical blocks in the filesystem.
|
|
lfs3_size_t block_count;
|
|
|
|
// Upper limit on the length of file names in bytes.
|
|
lfs3_size_t name_limit;
|
|
|
|
// Upper limit on the size of files in bytes.
|
|
lfs3_size_t file_limit;
|
|
};
|
|
|
|
// Traversal info structure
|
|
struct lfs3_tinfo {
|
|
// Type of the block
|
|
uint8_t btype;
|
|
|
|
// Block address
|
|
lfs3_block_t block;
|
|
};
|
|
|
|
// Custom attribute structure, used to describe custom attributes
|
|
// committed atomically during file writes.
|
|
struct lfs3_attr {
|
|
// Type of attribute
|
|
//
|
|
// Note some of this range is reserved:
|
|
// 0x00-0x7f - Free for custom attributes
|
|
// 0x80-0xff - May be assigned a standard attribute
|
|
uint8_t type;
|
|
|
|
// Flags that control how attr is read/written/removed
|
|
uint8_t flags;
|
|
|
|
// Pointer the buffer where the attr will be read/written
|
|
void *buffer;
|
|
|
|
// Size of the attr buffer in bytes, this can be set to
|
|
// LFS3_ERR_NOATTR to remove the attr
|
|
lfs3_ssize_t buffer_size;
|
|
|
|
// Optional pointer to a mutable attr size, updated on read/write,
|
|
// set to LFS3_ERR_NOATTR if attr does not exist
|
|
//
|
|
// Defaults to buffer_size if NULL
|
|
lfs3_ssize_t *size;
|
|
};
|
|
|
|
// Optional configuration provided during lfs3_file_opencfg
|
|
struct lfs3_file_cfg {
|
|
// Optional statically allocated file cache buffer. Must be fcache_size.
|
|
// By default lfs3_malloc is used to allocate this buffer.
|
|
void *fcache_buffer;
|
|
|
|
// Size of the file cache in bytes. In addition to filesystem-wide
|
|
// read/prog caches, each file gets its own cache to reduce disk
|
|
// accesses. Defaults to fcache_size if fcache_buffer is NULL.
|
|
lfs3_size_t fcache_size;
|
|
|
|
// Optional list of custom attributes attached to the file. If readable,
|
|
// these attributes will be kept up to date with the attributes on-disk.
|
|
// If writeable, these attributes will be written to disk atomically on
|
|
// every file sync or close.
|
|
struct lfs3_attr *attrs;
|
|
|
|
// Number of custom attributes in the list
|
|
lfs3_size_t attr_count;
|
|
};
|
|
|
|
|
|
/// internal littlefs data structures ///
|
|
|
|
// either an on-disk or in-RAM data pointer
|
|
//
|
|
// note, it's tempting to make this fancier, but we benefit quite a lot
|
|
// from the compiler being able to aggresively optimize this struct
|
|
//
|
|
typedef struct lfs3_data {
|
|
// sign2(size)=0b00 => in-RAM buffer
|
|
// sign2(size)=0b10 => on-disk data
|
|
// sign2(size)=0b11 => on-disk data + cksum
|
|
lfs3_size_t size;
|
|
union {
|
|
const uint8_t *buffer;
|
|
struct {
|
|
lfs3_block_t block;
|
|
lfs3_size_t off;
|
|
// optional context for validating data
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
// sign(cksize)=0 => block not erased
|
|
// sign(cksize)=1 => block erased
|
|
lfs3_size_t cksize;
|
|
uint32_t cksum;
|
|
#endif
|
|
} disk;
|
|
} u;
|
|
} lfs3_data_t;
|
|
|
|
// a possible block pointer
|
|
typedef struct lfs3_bptr {
|
|
// sign2(size)=0b00 => in-RAM buffer
|
|
// sign2(size)=0b10 => on-disk data
|
|
// sign2(size)=0b11 => block pointer
|
|
lfs3_data_t d;
|
|
#ifndef LFS3_CKDATACKSUMS
|
|
// sign(cksize)=0 => block not erased
|
|
// sign(cksize)=1 => block erased
|
|
lfs3_size_t cksize;
|
|
uint32_t cksum;
|
|
#endif
|
|
} lfs3_bptr_t;
|
|
|
|
// littlefs's core metadata log type
|
|
typedef struct lfs3_rbyd {
|
|
lfs3_rid_t weight;
|
|
lfs3_block_t blocks[2];
|
|
// sign(trunk)=0 => normal rbyd
|
|
// sign(trunk)=1 => shrub rbyd
|
|
lfs3_size_t trunk;
|
|
#ifndef LFS3_RDONLY
|
|
// sign(eoff) => perturb bit
|
|
// eoff=0, trunk=0 => not yet committed
|
|
// eoff=0, trunk>0 => not yet fetched
|
|
// eoff>=block_size => rbyd not erased/needs compaction
|
|
lfs3_size_t eoff;
|
|
#endif
|
|
uint32_t cksum;
|
|
} lfs3_rbyd_t;
|
|
|
|
// littlefs's btree representation
|
|
//
|
|
// technically all we need for btrees is the root rbyd, but tracking the
|
|
// most recent leaf helps speed up iteration/subattrs/etc without
|
|
// local rbyd allocations -- less code and stack for the same
|
|
// performance
|
|
typedef struct lfs3_btree {
|
|
lfs3_rbyd_t r;
|
|
#ifdef LFS3_BLEAFCACHE
|
|
struct {
|
|
lfs3_bid_t bid;
|
|
lfs3_rbyd_t r;
|
|
} leaf;
|
|
#endif
|
|
} lfs3_btree_t;
|
|
|
|
// littlefs's atomic metadata log type
|
|
typedef struct lfs3_mdir {
|
|
lfs3_smid_t mid;
|
|
lfs3_rbyd_t r;
|
|
uint32_t gcksumdelta;
|
|
} lfs3_mdir_t;
|
|
|
|
// a handle to an opened mdir for tracking purposes
|
|
typedef struct lfs3_handle {
|
|
// an invasive linked-list is used to keep things in-sync
|
|
struct lfs3_handle *next;
|
|
// flags includes the type and type-specific flags
|
|
uint32_t flags;
|
|
lfs3_mdir_t mdir;
|
|
} lfs3_handle_t;
|
|
|
|
// a shrub is a secondary trunk in an mdir
|
|
typedef lfs3_rbyd_t lfs3_shrub_t;
|
|
|
|
// a bshrub is like a btree but with a shrub as a root
|
|
typedef struct lfs3_bshrub {
|
|
// bshrubs need to be tracked for commits to work
|
|
lfs3_handle_t h;
|
|
// files contain both an active bshrub and staging bshrub, to allow
|
|
// staging during mdir compacts
|
|
// trunk=0 => no bshrub/btree
|
|
// sign(trunk)=1 => bshrub
|
|
// sign(trunk)=0 => btree
|
|
lfs3_btree_t b;
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_shrub_t b_;
|
|
#endif
|
|
} lfs3_bshrub_t;
|
|
|
|
// littlefs file type
|
|
typedef struct lfs3_file {
|
|
// btree/bshrub stuff is in here
|
|
lfs3_bshrub_t b;
|
|
const struct lfs3_file_cfg *cfg;
|
|
|
|
// current file position
|
|
lfs3_off_t pos;
|
|
|
|
// in-RAM cache
|
|
//
|
|
// note this lines up with lfs3_data_t's buffer representation
|
|
struct {
|
|
lfs3_off_t pos;
|
|
lfs3_off_t size;
|
|
uint8_t *buffer;
|
|
} cache;
|
|
|
|
// on-disk leaf bptr
|
|
struct {
|
|
lfs3_off_t pos;
|
|
lfs3_off_t weight;
|
|
lfs3_bptr_t bptr;
|
|
} leaf;
|
|
} lfs3_file_t;
|
|
|
|
// littlefs directory type
|
|
typedef struct lfs3_dir {
|
|
lfs3_handle_t h;
|
|
lfs3_did_t did;
|
|
lfs3_off_t pos;
|
|
} lfs3_dir_t;
|
|
|
|
// littlefs traversal type
|
|
typedef struct lfs3_btrv {
|
|
lfs3_sbid_t bid;
|
|
lfs3_rbyd_t rbyd;
|
|
lfs3_srid_t rid;
|
|
} lfs3_btrv_t;
|
|
|
|
typedef struct lfs3_mtortoise {
|
|
// this aligns with btrv.bid
|
|
lfs3_sbid_t bid;
|
|
lfs3_block_t blocks[2];
|
|
lfs3_block_t dist;
|
|
uint8_t nlog2;
|
|
} lfs3_mtortoise_t;
|
|
|
|
typedef struct lfs3_mtrv {
|
|
// mtree traversal state, our position in then handle linked-list
|
|
// is also used to keep track of what handles we've seen
|
|
lfs3_handle_t h;
|
|
// current bshrub/btree
|
|
lfs3_btree_t b;
|
|
union {
|
|
// bshrub/btree traversal state
|
|
lfs3_btrv_t btrv;
|
|
// mtortoise for cycle detection
|
|
lfs3_mtortoise_t mtortoise;
|
|
} u;
|
|
|
|
// recalculate gcksum when traversing with ckmeta
|
|
uint32_t gcksum;
|
|
} lfs3_mtrv_t;
|
|
|
|
typedef struct lfs3_mgc {
|
|
// core traversal state
|
|
lfs3_mtrv_t t;
|
|
|
|
#ifdef LFS3_GBMAP
|
|
// repopulate gbmap when traversing with regbmap
|
|
lfs3_btree_t gbmap_;
|
|
#endif
|
|
} lfs3_mgc_t;
|
|
|
|
typedef struct lfs3_trv {
|
|
// core traversal/gc state
|
|
lfs3_mgc_t gc;
|
|
|
|
// pending blocks, only used in lfs3_trv_read
|
|
lfs3_sblock_t blocks[2];
|
|
} lfs3_trv_t;
|
|
|
|
// littlefs global state
|
|
|
|
// grm encoding:
|
|
// .- -+- -+- -+- -+- -. mids: 2 leb128s <=2x5 bytes
|
|
// ' mids ' total: <=10 bytes
|
|
// + +
|
|
// ' '
|
|
// '- -+- -+- -+- -+- -'
|
|
//
|
|
#define LFS3_GRM_DSIZE (5+5)
|
|
|
|
typedef struct lfs3_grm {
|
|
lfs3_smid_t queue[2];
|
|
} lfs3_grm_t;
|
|
|
|
// gbmap encoding:
|
|
// .---+- -+- -+- -+- -. window: 1 leb128 <=5 bytes
|
|
// | window | known: 1 leb128 <=5 bytes
|
|
// +---+- -+- -+- -+- -+ block: 1 leb128 <=5 bytes
|
|
// | known | trunk: 1 leb128 <=4 bytes
|
|
// +---+- -+- -+- -+- -+ cksum: 1 le32 4 bytes
|
|
// | block | total: 23 bytes
|
|
// +---+- -+- -+- -+- -'
|
|
// | trunk |
|
|
// +---+- -+- -+- -+
|
|
// | cksum |
|
|
// '---+---+---+---'
|
|
#define LFS3_GBMAP_DSIZE (5+5+5+4+4)
|
|
|
|
typedef struct lfs3_gbmap {
|
|
lfs3_block_t window;
|
|
lfs3_block_t known;
|
|
lfs3_btree_t b;
|
|
lfs3_btree_t b_p;
|
|
} lfs3_gbmap_t;
|
|
|
|
|
|
// The littlefs filesystem type
|
|
typedef struct lfs3 {
|
|
const struct lfs3_cfg *cfg;
|
|
uint32_t flags;
|
|
lfs3_size_t block_count;
|
|
lfs3_size_t name_limit;
|
|
lfs3_off_t file_limit;
|
|
|
|
uint8_t mbits;
|
|
#ifndef LFS3_RDONLY
|
|
int8_t recycle_bits;
|
|
uint8_t rattr_estimate;
|
|
uint8_t mattr_estimate;
|
|
#endif
|
|
|
|
// linked-list of opened mdirs
|
|
lfs3_handle_t *handles;
|
|
|
|
lfs3_mdir_t mroot;
|
|
lfs3_btree_t mtree;
|
|
|
|
struct lfs3_rcache {
|
|
lfs3_block_t block;
|
|
lfs3_size_t off;
|
|
lfs3_size_t size;
|
|
uint8_t *buffer;
|
|
} rcache;
|
|
|
|
#ifndef LFS3_RDONLY
|
|
struct lfs3_pcache {
|
|
lfs3_block_t block;
|
|
lfs3_size_t off;
|
|
lfs3_size_t size;
|
|
uint8_t *buffer;
|
|
} pcache;
|
|
// optional prog-aligned cksum
|
|
uint32_t pcksum;
|
|
#endif
|
|
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_CKMETAPARITY)
|
|
struct {
|
|
lfs3_block_t block;
|
|
// sign(off) => tail parity
|
|
lfs3_size_t off;
|
|
} ptail;
|
|
#endif
|
|
|
|
#ifndef LFS3_RDONLY
|
|
struct lfs3_lookahead {
|
|
lfs3_block_t window;
|
|
lfs3_block_t off;
|
|
lfs3_block_t known;
|
|
lfs3_block_t ckpoint;
|
|
uint8_t *buffer;
|
|
} lookahead;
|
|
#endif
|
|
|
|
#ifndef LFS3_RDONLY
|
|
const lfs3_data_t *graft;
|
|
lfs3_ssize_t graft_count;
|
|
#endif
|
|
|
|
// global state
|
|
uint32_t gcksum;
|
|
#ifndef LFS3_RDONLY
|
|
uint32_t gcksum_p;
|
|
#endif
|
|
// TODO can we actually get rid of grm_d when LFS3_RDONLY?
|
|
uint32_t gcksum_d;
|
|
|
|
lfs3_grm_t grm;
|
|
#ifndef LFS3_RDONLY
|
|
uint8_t grm_p[LFS3_GRM_DSIZE];
|
|
#endif
|
|
// TODO can we actually get rid of grm_d when LFS3_RDONLY?
|
|
uint8_t grm_d[LFS3_GRM_DSIZE];
|
|
|
|
#ifdef LFS3_GBMAP
|
|
lfs3_gbmap_t gbmap;
|
|
uint8_t gbmap_p[LFS3_GBMAP_DSIZE];
|
|
uint8_t gbmap_d[LFS3_GBMAP_DSIZE];
|
|
#endif
|
|
|
|
// optional incremental gc state
|
|
#ifdef LFS3_GC
|
|
lfs3_mgc_t gc;
|
|
#endif
|
|
} lfs3_t;
|
|
|
|
|
|
/// Filesystem functions ///
|
|
|
|
// Format a block device with the littlefs
|
|
//
|
|
// Requires a littlefs object and config struct. This clobbers the littlefs
|
|
// object, and does not leave the filesystem mounted. The config struct must
|
|
// be zeroed for defaults and backwards compatibility.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_format(lfs3_t *lfs3, uint32_t flags,
|
|
const struct lfs3_cfg *cfg);
|
|
#endif
|
|
|
|
// Mounts a littlefs
|
|
//
|
|
// Requires a littlefs object and config struct. Multiple filesystems
|
|
// may be mounted simultaneously with multiple littlefs objects. Both
|
|
// lfs3 and config must be allocated while mounted. The config struct must
|
|
// be zeroed for defaults and backwards compatibility.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_mount(lfs3_t *lfs3, uint32_t flags,
|
|
const struct lfs3_cfg *cfg);
|
|
|
|
// Unmounts a littlefs
|
|
//
|
|
// Does nothing besides releasing any allocated resources.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_unmount(lfs3_t *lfs3);
|
|
|
|
/// General operations ///
|
|
|
|
// Get the value of a file
|
|
//
|
|
// Returns the number of bytes read, or a negative error code on failure.
|
|
// Note this may be less than the on-disk file size if the buffer is not
|
|
// large enough.
|
|
lfs3_ssize_t lfs3_get(lfs3_t *lfs3, const char *path,
|
|
void *buffer, lfs3_size_t size);
|
|
|
|
// Get a file's size
|
|
//
|
|
// Returns the size of the file, or a negative error code on failure.
|
|
lfs3_ssize_t lfs3_size(lfs3_t *lfs3, const char *path);
|
|
|
|
// Set the value of a file
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_set(lfs3_t *lfs3, const char *path,
|
|
const void *buffer, lfs3_size_t size);
|
|
#endif
|
|
|
|
// Removes a file or directory
|
|
//
|
|
// If removing a directory, the directory must be empty.
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_remove(lfs3_t *lfs3, const char *path);
|
|
#endif
|
|
|
|
// Rename or move a file or directory
|
|
//
|
|
// If the destination exists, it must match the source in type.
|
|
// If the destination is a directory, the directory must be empty.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_rename(lfs3_t *lfs3, const char *old_path, const char *new_path);
|
|
#endif
|
|
|
|
// Find info about a file or directory
|
|
//
|
|
// Fills out the info structure, based on the specified file or directory.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_stat(lfs3_t *lfs3, const char *path, struct lfs3_info *info);
|
|
|
|
// Get a custom attribute
|
|
//
|
|
// Returns the number of bytes read, or a negative error code on failure.
|
|
// Note this may be less than the on-disk attr size if the buffer is not
|
|
// large enough.
|
|
lfs3_ssize_t lfs3_getattr(lfs3_t *lfs3, const char *path, uint8_t type,
|
|
void *buffer, lfs3_size_t size);
|
|
|
|
// Get a custom attribute's size
|
|
//
|
|
// Returns the size of the attribute, or a negative error code on failure.
|
|
lfs3_ssize_t lfs3_sizeattr(lfs3_t *lfs3, const char *path, uint8_t type);
|
|
|
|
// Set a custom attributes
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_setattr(lfs3_t *lfs3, const char *path, uint8_t type,
|
|
const void *buffer, lfs3_size_t size);
|
|
#endif
|
|
|
|
// Removes a custom attribute
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_removeattr(lfs3_t *lfs3, const char *path, uint8_t type);
|
|
#endif
|
|
|
|
|
|
/// File operations ///
|
|
|
|
// Open a file
|
|
//
|
|
// The mode that the file is opened in is determined by the flags, which
|
|
// are values from the enum lfs3_open_flags that are bitwise-ored together.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_NO_MALLOC
|
|
int lfs3_file_open(lfs3_t *lfs3, lfs3_file_t *file,
|
|
const char *path, uint32_t flags);
|
|
#endif
|
|
|
|
// Open a file with extra configuration
|
|
//
|
|
// The mode that the file is opened in is determined by the flags, which
|
|
// are values from the enum lfs3_open_flags that are bitwise-ored together.
|
|
//
|
|
// The config struct provides additional config options per file as described
|
|
// above. The config struct must remain allocated while the file is open, and
|
|
// the config struct must be zeroed for defaults and backwards compatibility.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_opencfg(lfs3_t *lfs3, lfs3_file_t *file,
|
|
const char *path, uint32_t flags,
|
|
const struct lfs3_file_cfg *cfg);
|
|
|
|
// Close a file
|
|
//
|
|
// If the file is not desynchronized, any pending writes are written out
|
|
// to storage as though sync had been called.
|
|
//
|
|
// Releases any allocated resources, even if there is an error.
|
|
//
|
|
// Readonly and desynchronized files do not touch disk and will always
|
|
// return 0.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_close(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Synchronize a file on storage
|
|
//
|
|
// Any pending writes are written out to storage and other open files.
|
|
//
|
|
// If the file was desynchronized, it is now marked as synchronized. It will
|
|
// now recieve file updates and syncs on close.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_sync(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Flush any buffered data
|
|
//
|
|
// This does not update metadata and is called implicitly by lfs3_file_sync.
|
|
// Calling this explicitly may be useful for preventing write errors in
|
|
// read operations.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_flush(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Mark a file as desynchronized
|
|
//
|
|
// Desynchronized files do not recieve file updates and do not sync on close.
|
|
// They effectively act as snapshots of the underlying file at that point
|
|
// in time.
|
|
//
|
|
// If an error occurs during a write operation, the file is implicitly marked
|
|
// as desynchronized.
|
|
//
|
|
// An explicit and successful call to either lfs3_file_sync or
|
|
// lfs3_file_resync reverses this, marking the file as synchronized again.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_desync(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Discard unsynchronized changes and mark a file as synchronized
|
|
//
|
|
// This is effectively the same as closing and reopening the file, and
|
|
// may read from disk to figure out file state.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_resync(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Read data from file
|
|
//
|
|
// Takes a buffer and size indicating where to store the read data.
|
|
// Returns the number of bytes read, or a negative error code on failure.
|
|
lfs3_ssize_t lfs3_file_read(lfs3_t *lfs3, lfs3_file_t *file,
|
|
void *buffer, lfs3_size_t size);
|
|
|
|
// Write data to file
|
|
//
|
|
// Takes a buffer and size indicating the data to write. The file will not
|
|
// actually be updated on the storage until either sync or close is called.
|
|
//
|
|
// Returns the number of bytes written, or a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
lfs3_ssize_t lfs3_file_write(lfs3_t *lfs3, lfs3_file_t *file,
|
|
const void *buffer, lfs3_size_t size);
|
|
#endif
|
|
|
|
// Change the position of the file
|
|
//
|
|
// The change in position is determined by the offset and whence flag.
|
|
// Returns the new position of the file, or a negative error code on failure.
|
|
lfs3_soff_t lfs3_file_seek(lfs3_t *lfs3, lfs3_file_t *file,
|
|
lfs3_soff_t off, uint8_t whence);
|
|
|
|
// Truncate/grow the size of the file to the specified size
|
|
//
|
|
// If size is larger than the current file size, a hole is created, appearing
|
|
// as if the file was filled with zeros.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_file_truncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size);
|
|
#endif
|
|
|
|
// Truncate/grow the file, but from the front
|
|
//
|
|
// If size is larger than the current file size, a hole is created, appearing
|
|
// as if the file was filled with zeros.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_file_fruncate(lfs3_t *lfs3, lfs3_file_t *file, lfs3_off_t size);
|
|
#endif
|
|
|
|
// Return the position of the file
|
|
//
|
|
// Equivalent to lfs3_file_seek(lfs3, file, 0, LFS3_SEEK_CUR)
|
|
// Returns the position of the file, or a negative error code on failure.
|
|
lfs3_soff_t lfs3_file_tell(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Change the position of the file to the beginning of the file
|
|
//
|
|
// Equivalent to lfs3_file_seek(lfs3, file, 0, LFS3_SEEK_SET)
|
|
// Returns a negative error code on failure.
|
|
int lfs3_file_rewind(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Return the size of the file
|
|
//
|
|
// Similar to lfs3_file_seek(lfs3, file, 0, LFS3_SEEK_END)
|
|
// Returns the size of the file, or a negative error code on failure.
|
|
lfs3_soff_t lfs3_file_size(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Check a file for metadata errors
|
|
//
|
|
// Returns LFS3_ERR_CORRUPT if a checksum mismatch is found, or a negative
|
|
// error code on failure.
|
|
int lfs3_file_ckmeta(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
// Check a file for metadata + data errors
|
|
//
|
|
// Returns LFS3_ERR_CORRUPT if a checksum mismatch is found, or a negative
|
|
// error code on failure.
|
|
int lfs3_file_ckdata(lfs3_t *lfs3, lfs3_file_t *file);
|
|
|
|
|
|
/// Directory operations ///
|
|
|
|
// Create a directory
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_mkdir(lfs3_t *lfs3, const char *path);
|
|
#endif
|
|
|
|
// Open a directory
|
|
//
|
|
// Once open a directory can be used with read to iterate over files.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_dir_open(lfs3_t *lfs3, lfs3_dir_t *dir, const char *path);
|
|
|
|
// Close a directory
|
|
//
|
|
// Releases any allocated resources.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_dir_close(lfs3_t *lfs3, lfs3_dir_t *dir);
|
|
|
|
// Read an entry in the directory
|
|
//
|
|
// Fills out the info structure, based on the specified file or directory.
|
|
// Returns 0 on success, LFS3_ERR_NOENT at the end of directory, or a
|
|
// negative error code on failure.
|
|
int lfs3_dir_read(lfs3_t *lfs3, lfs3_dir_t *dir, struct lfs3_info *info);
|
|
|
|
// Change the position of the directory
|
|
//
|
|
// The new off must be a value previous returned from tell and specifies
|
|
// an absolute offset in the directory seek.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_dir_seek(lfs3_t *lfs3, lfs3_dir_t *dir, lfs3_soff_t off);
|
|
|
|
// Return the position of the directory
|
|
//
|
|
// The returned offset is only meant to be consumed by seek and may not make
|
|
// sense, but does indicate the current position in the directory iteration.
|
|
//
|
|
// Returns the position of the directory, or a negative error code on failure.
|
|
lfs3_soff_t lfs3_dir_tell(lfs3_t *lfs3, lfs3_dir_t *dir);
|
|
|
|
// Change the position of the directory to the beginning of the directory
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_dir_rewind(lfs3_t *lfs3, lfs3_dir_t *dir);
|
|
|
|
|
|
/// Traversal operations ///
|
|
|
|
// Open a traversal
|
|
//
|
|
// Once open, a traversal can be read from to iterate over all blocks in
|
|
// the filesystem.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_trv_open(lfs3_t *lfs3, lfs3_trv_t *trv, uint32_t flags);
|
|
|
|
// Close a traversal
|
|
//
|
|
// Releases any allocated resources.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_trv_close(lfs3_t *lfs3, lfs3_trv_t *trv);
|
|
|
|
// Progress the traversal and read an entry
|
|
//
|
|
// Fills out the tinfo structure.
|
|
//
|
|
// Returns 0 on success, LFS3_ERR_NOENT at the end of traversal, or a
|
|
// negative error code on failure.
|
|
int lfs3_trv_read(lfs3_t *lfs3, lfs3_trv_t *trv,
|
|
struct lfs3_tinfo *tinfo);
|
|
|
|
// Reset the traversal
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_trv_rewind(lfs3_t *lfs3, lfs3_trv_t *trv);
|
|
|
|
|
|
/// Filesystem-level filesystem operations
|
|
|
|
// Find on-disk info about the filesystem
|
|
//
|
|
// Fills out the fsinfo structure based on the filesystem found on-disk.
|
|
// Returns a negative error code on failure.
|
|
int lfs3_fs_stat(lfs3_t *lfs3, struct lfs3_fsinfo *fsinfo);
|
|
|
|
// Finds the number of blocks in use by the filesystem
|
|
//
|
|
// Note: Result is best effort. If files share COW structures, the returned
|
|
// usage may be larger than the filesystem actually is.
|
|
//
|
|
// Returns the number of allocated blocks, or a negative error code on failure.
|
|
lfs3_ssize_t lfs3_fs_usage(lfs3_t *lfs3);
|
|
|
|
// Attempt to make the filesystem consistent and ready for writing
|
|
//
|
|
// Calling this function is not required, consistency will be implicitly
|
|
// enforced on the first operation that writes to the filesystem, but this
|
|
// function allows the work to be performed earlier and without other
|
|
// filesystem changes.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_fs_mkconsistent(lfs3_t *lfs3);
|
|
#endif
|
|
|
|
// Check the filesystem for metadata errors
|
|
//
|
|
// Returns LFS3_ERR_CORRUPT if a checksum mismatch is found, or a negative
|
|
// error code on failure.
|
|
int lfs3_fs_ckmeta(lfs3_t *lfs3);
|
|
|
|
// Check the filesystem for metadata + data errors
|
|
//
|
|
// Returns LFS3_ERR_CORRUPT if a checksum mismatch is found, or a negative
|
|
// error code on failure.
|
|
int lfs3_fs_ckdata(lfs3_t *lfs3);
|
|
|
|
// Get the current filesystem checksum
|
|
//
|
|
// This is a checksum of all metadata + data in the filesystem, which
|
|
// can be stored externally to provide increased protection against
|
|
// filesystem corruption.
|
|
//
|
|
// Note this checksum is order-sensitive. So while it's unlikely two
|
|
// filesystems with different contents will have the same checksum, two
|
|
// filesystems with the same contents may not have the same checksum.
|
|
//
|
|
// Also note this is only a 32-bit checksum. Collisions should be
|
|
// expected.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_fs_cksum(lfs3_t *lfs3, uint32_t *cksum);
|
|
|
|
// Perform any janitorial work that may be pending
|
|
//
|
|
// The exact janitorial work depends on the configured flags and steps.
|
|
//
|
|
// Calling this function is not required, but may allow the offloading of
|
|
// expensive janitorial work to a less time-critical code path.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifdef LFS3_GC
|
|
int lfs3_fs_gc(lfs3_t *lfs3);
|
|
#endif
|
|
|
|
// Mark janitorial work as incomplete
|
|
//
|
|
// Any info flags passed to lfs3_gc_unck will be reset internally,
|
|
// forcing the work to be redone.
|
|
//
|
|
// This is most useful for triggering new ckmeta/ckdata scans with
|
|
// LFS3_I_CANCKMETA and LFS3_I_CANCKDATA. Otherwise littlefs will perform
|
|
// only one scan after mount.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
int lfs3_fs_unck(lfs3_t *lfs3, uint32_t flags);
|
|
|
|
// Change the number of blocks used by the filesystem
|
|
//
|
|
// This changes the number of blocks we are currently using and updates
|
|
// the superblock with the new block count.
|
|
//
|
|
// Note: This is irreversible.
|
|
//
|
|
// Returns a negative error code on failure.
|
|
#ifndef LFS3_RDONLY
|
|
int lfs3_fs_grow(lfs3_t *lfs3, lfs3_size_t block_count);
|
|
#endif
|
|
|
|
// Enable the global on-disk block-map
|
|
//
|
|
// Returns a negative error code on failure. Does nothing if a gbmap
|
|
// already exists.
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP)
|
|
int lfs3_fs_mkgbmap(lfs3_t *lfs3);
|
|
#endif
|
|
|
|
// Disable the global on-disk block-map
|
|
//
|
|
// Returns a negative error code on failure. Does nothing if no gbmap
|
|
// is found.
|
|
#if !defined(LFS3_RDONLY) && defined(LFS3_GBMAP) && !defined(LFS3_YES_GBMAP)
|
|
int lfs3_fs_rmgbmap(lfs3_t *lfs3);
|
|
#endif
|
|
|
|
|
|
#endif
|