Simplified config

Before, the lfs had multiple paths to determine config options:
- lfs_config struct passed during initialization
- lfs_bd_info struct passed during block device initialization
- compile time options

This allowed different developers to provide their own needs
to the filesystem, such as the block device capabilities and
the higher level user's own tweaks.

However, this comes with additional complexity and action required
when the configurations are incompatible.

For now, this has been reduced to all information (including block
device function pointers) being passed through the lfs_config struct.
We just defer more complicated handling of configuration options to
the top level user.

This simplifies configuration handling and gives the top level user
the responsibility to handle configuration, which they probably would
have wanted to do anyways.
This commit is contained in:
Christopher Haster
2017-04-22 11:42:05 -05:00
parent 3b9d6630c8
commit 789286a257
13 changed files with 254 additions and 416 deletions

View File

@@ -19,9 +19,12 @@
// Block device emulated on existing filesystem
int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
memset(&emu->info, 0, sizeof(emu->info));
memset(&emu->stats, 0, sizeof(emu->stats));
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
lfs_emubd_t *emu = cfg->context;
emu->cfg.read_size = cfg->read_size;
emu->cfg.prog_size = cfg->prog_size;
emu->cfg.block_size = cfg->block_size;
emu->cfg.block_count = cfg->block_count;
// Allocate buffer for creating children files
size_t pathlen = strlen(path);
@@ -41,12 +44,6 @@ int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
return -errno;
}
// Setup info based on configuration
emu->info.read_size = LFS_EMUBD_READ_SIZE;
emu->info.prog_size = LFS_EMUBD_PROG_SIZE;
emu->info.erase_size = LFS_EMUBD_ERASE_SIZE;
emu->info.total_size = LFS_EMUBD_TOTAL_SIZE;
// Load stats to continue incrementing
snprintf(emu->child, LFS_NAME_MAX, "stats");
FILE *f = fopen(emu->path, "r");
@@ -67,92 +64,42 @@ int lfs_emubd_create(lfs_emubd_t *emu, const char *path) {
return 0;
}
void lfs_emubd_destroy(lfs_emubd_t *emu) {
lfs_emubd_sync(emu);
void lfs_emubd_destroy(const struct lfs_config *cfg) {
lfs_emubd_sync(cfg);
lfs_emubd_t *emu = cfg->context;
free(emu->path);
}
int lfs_emubd_read(lfs_emubd_t *emu, lfs_block_t block,
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) {
lfs_emubd_t *emu = cfg->context;
uint8_t *data = buffer;
// Check if read is valid
assert(off % emu->info.read_size == 0);
assert(size % emu->info.read_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size
< emu->info.total_size);
assert(off % cfg->read_size == 0);
assert(size % cfg->read_size == 0);
assert(block < cfg->block_count);
// Zero out buffer for debugging
memset(data, 0, size);
// Iterate over blocks until enough data is read
while (size > 0) {
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
size_t count = lfs_min(emu->info.erase_size - off, size);
// Read data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) {
return -errno;
}
if (f) {
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fread(data, 1, count, f);
if (res < count && !feof(f)) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
size -= count;
data += count;
block += 1;
off = 0;
FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) {
return -errno;
}
emu->stats.read_count += 1;
return 0;
}
int lfs_emubd_prog(lfs_emubd_t *emu, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
const uint8_t *data = buffer;
// Check if write is valid
assert(off % emu->info.prog_size == 0);
assert(size % emu->info.prog_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size
< emu->info.total_size);
// Iterate over blocks until enough data is read
while (size > 0) {
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
size_t count = lfs_min(emu->info.erase_size - off, size);
FILE *f = fopen(emu->path, "r+b");
if (!f && errno == ENOENT) {
f = fopen(emu->path, "w+b");
if (!f) {
return -errno;
}
}
if (f) {
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fwrite(data, 1, count, f);
if (res < count) {
size_t res = fread(data, 1, size, f);
if (res < size && !feof(f)) {
return -errno;
}
@@ -160,60 +107,88 @@ int lfs_emubd_prog(lfs_emubd_t *emu, lfs_block_t block,
if (err) {
return -errno;
}
}
size -= count;
data += count;
block += 1;
off = 0;
emu->stats.read_count += 1;
return 0;
}
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
lfs_emubd_t *emu = cfg->context;
const uint8_t *data = buffer;
// Check if write is valid
assert(off % cfg->prog_size == 0);
assert(size % cfg->prog_size == 0);
assert(block < cfg->block_count);
// Program data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
FILE *f = fopen(emu->path, "r+b");
if (!f && errno == ENOENT) {
f = fopen(emu->path, "w+b");
if (!f) {
return -errno;
}
}
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fwrite(data, 1, size, f);
if (res < size) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
emu->stats.prog_count += 1;
return 0;
}
int lfs_emubd_erase(lfs_emubd_t *emu, lfs_block_t block,
lfs_off_t off, lfs_size_t size) {
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
lfs_emubd_t *emu = cfg->context;
// Check if erase is valid
assert(off % emu->info.erase_size == 0);
assert(size % emu->info.erase_size == 0);
assert((uint64_t)block*emu->info.erase_size + off + size
< emu->info.total_size);
assert(block < cfg->block_count);
// Iterate and erase blocks
while (size > 0) {
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
struct stat st;
int err = stat(emu->path, &st);
if (err && errno != ENOENT) {
// Erase the block
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
struct stat st;
int err = stat(emu->path, &st);
if (err && errno != ENOENT) {
return -errno;
}
if (!err && S_ISREG(st.st_mode)) {
int err = unlink(emu->path);
if (err) {
return -errno;
}
if (!err && S_ISREG(st.st_mode)) {
int err = unlink(emu->path);
if (err) {
return -errno;
}
}
size -= emu->info.erase_size;
block += 1;
off = 0;
}
emu->stats.erase_count += 1;
return 0;
}
int lfs_emubd_sync(lfs_emubd_t *emu) {
int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, "info");
snprintf(emu->child, LFS_NAME_MAX, "config");
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
size_t res = fwrite(&emu->info, sizeof(emu->info), 1, f);
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
if (res < 1) {
return -errno;
}
@@ -242,46 +217,3 @@ int lfs_emubd_sync(lfs_emubd_t *emu) {
return 0;
}
int lfs_emubd_info(lfs_emubd_t *emu, struct lfs_bd_info *info) {
*info = emu->info;
return 0;
}
int lfs_emubd_stats(lfs_emubd_t *emu, struct lfs_bd_stats *stats) {
*stats = emu->stats;
return 0;
}
// Wrappers for void*s
static int lfs_emubd_bd_read(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) {
return lfs_emubd_read((lfs_emubd_t*)bd, block, off, size, buffer);
}
static int lfs_emubd_bd_prog(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
return lfs_emubd_prog((lfs_emubd_t*)bd, block, off, size, buffer);
}
static int lfs_emubd_bd_erase(void *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size) {
return lfs_emubd_erase((lfs_emubd_t*)bd, block, off, size);
}
static int lfs_emubd_bd_sync(void *bd) {
return lfs_emubd_sync((lfs_emubd_t*)bd);
}
static int lfs_emubd_bd_info(void *bd, struct lfs_bd_info *info) {
return lfs_emubd_info((lfs_emubd_t*)bd, info);
}
const struct lfs_bd_ops lfs_emubd_ops = {
.read = lfs_emubd_bd_read,
.prog = lfs_emubd_bd_prog,
.erase = lfs_emubd_bd_erase,
.sync = lfs_emubd_bd_sync,
.info = lfs_emubd_bd_info,
};

View File

@@ -7,9 +7,8 @@
#ifndef LFS_EMUBD_H
#define LFS_EMUBD_H
#include "lfs_config.h"
#include "lfs.h"
#include "lfs_util.h"
#include "lfs_bd.h"
// Config options
@@ -30,60 +29,50 @@
#endif
// Stats for debugging and optimization
struct lfs_bd_stats {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
};
// The emu bd state
typedef struct lfs_emubd {
char *path;
char *child;
struct lfs_bd_info info;
struct lfs_bd_stats stats;
struct {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
} stats;
struct {
uint32_t read_size;
uint32_t prog_size;
uint32_t block_size;
uint32_t block_count;
} cfg;
} lfs_emubd_t;
// Create a block device using path for the directory to store blocks
int lfs_emubd_create(lfs_emubd_t *emu, const char *path);
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
// Clean up memory associated with emu block device
void lfs_emubd_destroy(lfs_emubd_t *emu);
void lfs_emubd_destroy(const struct lfs_config *cfg);
// Read a block
int lfs_emubd_read(lfs_emubd_t *bd, lfs_block_t block,
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer);
// Program a block
//
// The block must have previously been erased.
int lfs_emubd_prog(lfs_emubd_t *bd, lfs_block_t block,
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_emubd_erase(lfs_emubd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size);
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
// Sync the block device
int lfs_emubd_sync(lfs_emubd_t *bd);
// Get a description of the block device
//
// Any unknown information may be left unmodified
int lfs_emubd_info(lfs_emubd_t *bd, struct lfs_bd_info *info);
// Get stats of operations on the block device
//
// Used for debugging and optimizations
int lfs_emubd_stats(lfs_emubd_t *bd, struct lfs_bd_stats *stats);
// Block device operations
extern const struct lfs_bd_ops lfs_emubd_ops;
int lfs_emubd_sync(const struct lfs_config *cfg);
#endif

141
lfs.c
View File

@@ -12,27 +12,22 @@
/// Block device operations ///
static int lfs_bd_info(lfs_t *lfs, struct lfs_bd_info *info) {
return lfs->bd_ops->info(lfs->bd, info);
}
static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer) {
return lfs->bd_ops->read(lfs->bd, block, off, size, buffer);
return lfs->cfg->read(lfs->cfg, block, off, size, buffer);
}
static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer) {
return lfs->bd_ops->prog(lfs->bd, block, off, size, buffer);
return lfs->cfg->prog(lfs->cfg, block, off, size, buffer);
}
static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, lfs_size_t size) {
return lfs->bd_ops->erase(lfs->bd, block, off, size);
static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
return lfs->cfg->erase(lfs->cfg, block);
}
static int lfs_bd_sync(lfs_t *lfs) {
return lfs->bd_ops->sync(lfs->bd);
return lfs->cfg->sync(lfs->cfg);
}
static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
@@ -101,7 +96,7 @@ static int lfs_alloc_scan(lfs_t *lfs) {
// found free block, now find stride of free blocks
// since this is relatively cheap (stress on relatively)
lfs->free.begin += off;
lfs->free.end = lfs->block_count; // before superblock
lfs->free.end = lfs->cfg->block_count; // before superblock
// find maximum stride in tree
return lfs_traverse(lfs, lfs_alloc_stride, lfs);
@@ -157,7 +152,7 @@ static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
return err;
}
return lfs_bd_erase(lfs, *block, 0, lfs->block_size);
return lfs_bd_erase(lfs, *block);
}
@@ -175,9 +170,9 @@ static lfs_off_t lfs_indexnext(lfs_t *lfs, lfs_off_t ioff) {
static lfs_off_t lfs_indexfrom(lfs_t *lfs, lfs_off_t off) {
lfs_off_t i = 0;
while (off > lfs->block_size) {
while (off > lfs->cfg->block_size) {
i = lfs_indexnext(lfs, i);
off -= lfs->block_size;
off -= lfs->cfg->block_size;
}
return i;
@@ -370,7 +365,7 @@ static int lfs_dir_fetch(lfs_t *lfs,
uint32_t crc = 0xffffffff;
crc = lfs_crc(crc, sizeof(test), &test);
for (lfs_off_t j = sizeof(test); j < lfs->block_size; j += 4) {
for (lfs_off_t j = sizeof(test); j < lfs->cfg->block_size; j += 4) {
uint32_t word;
int err = lfs_bd_read(lfs, tpair[i], j, 4, &word);
if (err) {
@@ -406,7 +401,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
dir->d.rev += 1;
lfs_pairswap(dir->pair);
int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size);
int err = lfs_bd_erase(lfs, dir->pair[0]);
if (err) {
return err;
}
@@ -456,7 +451,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
}
}
while (off < lfs->block_size-4) {
while (off < lfs->cfg->block_size-4) {
uint8_t data;
int err = lfs_bd_read(lfs, dir->pair[0], off, 1, &data);
if (err) {
@@ -467,7 +462,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
off += 1;
}
err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc);
err = lfs_bd_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, 4, &crc);
if (err) {
return err;
}
@@ -480,7 +475,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
dir->d.size -= entry->d.len;
lfs_pairswap(dir->pair);
int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size);
int err = lfs_bd_erase(lfs, dir->pair[0]);
if (err) {
return err;
}
@@ -516,7 +511,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
}
}
while (woff < lfs->block_size-4) {
while (woff < lfs->cfg->block_size-4) {
uint8_t data;
int err = lfs_bd_read(lfs, dir->pair[0], woff, 1, &data);
if (err) {
@@ -527,7 +522,7 @@ static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
woff += 1;
}
err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc);
err = lfs_bd_prog(lfs, dir->pair[0], lfs->cfg->block_size-4, 4, &crc);
if (err) {
return err;
}
@@ -539,7 +534,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, const void *data) {
// check if we fit, if top bit is set we do not and move on
while (true) {
if (dir->d.size + entry->d.len <= lfs->block_size - 4) {
if (dir->d.size + entry->d.len <= lfs->cfg->block_size - 4) {
entry->pair[0] = dir->pair[0];
entry->pair[1] = dir->pair[1];
entry->off = dir->d.size;
@@ -882,7 +877,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
// TODO do this lazily in write?
// TODO cow the head i/d block
if (file->size < lfs->block_size) {
if (file->size < lfs->cfg->block_size) {
file->wblock = file->head;
} else {
int err = lfs_index_find(lfs, file->head, file->windex,
@@ -915,7 +910,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
lfs_size_t nsize = size;
while (nsize > 0) {
lfs_off_t woff = file->size % lfs->block_size;
lfs_off_t woff = file->size % lfs->cfg->block_size;
if (file->size == 0) {
int err = lfs_alloc_erased(lfs, &file->wblock);
@@ -938,7 +933,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
}
}
lfs_size_t diff = lfs_min(nsize, lfs->block_size - woff);
lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - woff);
int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data);
if (err) {
return err;
@@ -958,10 +953,10 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
lfs_size_t nsize = size;
while (nsize > 0 && file->roff < file->size) {
lfs_off_t roff = file->roff % lfs->block_size;
lfs_off_t roff = file->roff % lfs->cfg->block_size;
// TODO cache index blocks
if (file->size < lfs->block_size) {
if (file->size < lfs->cfg->block_size) {
file->rblock = file->head;
} else if (roff == 0) {
int err = lfs_index_find(lfs, file->head, file->windex,
@@ -975,7 +970,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
lfs_size_t diff = lfs_min(
lfs_min(nsize, file->size-file->roff),
lfs->block_size - roff);
lfs->cfg->block_size - roff);
int err = lfs_bd_read(lfs, file->rblock, roff, diff, data);
if (err) {
return err;
@@ -991,85 +986,17 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
/// Generic filesystem operations ///
static int lfs_configure(lfs_t *lfs, const struct lfs_config *config) {
lfs->bd = config->bd;
lfs->bd_ops = config->bd_ops;
struct lfs_bd_info info;
int err = lfs_bd_info(lfs, &info);
if (err) {
return err;
}
if (config->read_size) {
if (config->read_size < info.read_size ||
config->read_size % info.read_size != 0) {
LFS_ERROR("Invalid read size %u, device has %u\n",
config->read_size, info.read_size);
return LFS_ERROR_INVALID;
}
lfs->read_size = config->read_size;
} else {
lfs->read_size = info.read_size;
}
if (config->prog_size) {
if (config->prog_size < info.prog_size ||
config->prog_size % info.prog_size != 0) {
LFS_ERROR("Invalid prog size %u, device has %u\n",
config->prog_size, info.prog_size);
return LFS_ERROR_INVALID;
}
lfs->prog_size = config->prog_size;
} else {
lfs->prog_size = info.prog_size;
}
if (config->block_size) {
if (config->block_size < info.erase_size ||
config->block_size % info.erase_size != 0) {
LFS_ERROR("Invalid block size %u, device has %u\n",
config->prog_size, info.prog_size);
return LFS_ERROR_INVALID;
}
lfs->block_size = config->block_size;
} else {
lfs->block_size = lfs_min(512, info.erase_size);
}
if (config->block_count) {
if (config->block_count > info.total_size/info.erase_size) {
LFS_ERROR("Invalid block size %u, device has %u\n",
config->block_size,
(uint32_t)(info.total_size/info.erase_size));
return LFS_ERROR_INVALID;
}
lfs->block_count = config->block_count;
} else {
lfs->block_count = info.total_size / info.erase_size;
}
lfs->words = lfs->block_size / sizeof(uint32_t);
return 0;
}
int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
int err = lfs_configure(lfs, config);
if (err) {
return err;
}
lfs->cfg = config;
lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
// Create free list
lfs->free.begin = 0;
lfs->free.end = lfs->block_count-1;
lfs->free.end = lfs->cfg->block_count-1;
// Create superblock dir
lfs_dir_t superdir;
err = lfs_dir_alloc(lfs, &superdir);
int err = lfs_dir_alloc(lfs, &superdir);
if (err) {
return err;
}
@@ -1096,8 +1023,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
.d.len = sizeof(superblock.d),
.d.version = 0x00000001,
.d.magic = {"littlefs"},
.d.block_size = lfs->block_size,
.d.block_count = lfs->block_count,
.d.block_size = lfs->cfg->block_size,
.d.block_count = lfs->cfg->block_count,
.d.root = {lfs->root[0], lfs->root[1]},
};
superdir.d.tail[0] = root.pair[0];
@@ -1121,14 +1048,12 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
}
int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
int err = lfs_configure(lfs, config);
if (err) {
return err;
}
lfs->cfg = config;
lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
lfs_dir_t dir;
lfs_superblock_t superblock;
err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
if (!err) {
err = lfs_bd_read(lfs, dir.pair[0],
sizeof(dir.d), sizeof(superblock.d), &superblock.d);
@@ -1186,7 +1111,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
dir.off += file.entry.d.len;
if ((0xf & file.entry.d.type) == LFS_TYPE_REG) {
if (file.entry.d.u.file.size < lfs->block_size) {
if (file.entry.d.u.file.size < lfs->cfg->block_size) {
int err = cb(data, file.entry.d.u.file.head);
if (err) {
return err;

51
lfs.h
View File

@@ -8,10 +8,9 @@
#define LFS_H
#include "lfs_config.h"
#include "lfs_bd.h"
// Data structures
// The littefs constants
enum lfs_error {
LFS_ERROR_OK = 0,
LFS_ERROR_CORRUPT = -3,
@@ -41,23 +40,56 @@ enum lfs_open_flags {
};
// Configuration provided during initialization of the littlefs
struct lfs_config {
lfs_bd_t *bd;
const struct lfs_bd_ops *bd_ops;
// 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, lfs_size_t size, void *buffer);
// 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, lfs_size_t size, const void *buffer);
// 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;
};
// 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];
};
// Internal data structures
typedef struct lfs_entry {
lfs_block_t pair[2];
lfs_off_t off;
@@ -115,17 +147,12 @@ typedef struct lfs_superblock {
} d;
} lfs_superblock_t;
// Little filesystem type
typedef struct lfs {
lfs_size_t read_size; // size of read
lfs_size_t prog_size; // size of program
lfs_size_t block_size; // size of erase (block size)
lfs_size_t block_count; // number of erasable blocks
const struct lfs_config *cfg;
lfs_size_t words; // number of 32-bit words that can fit in a block
lfs_bd_t *bd;
const struct lfs_bd_ops *bd_ops;
lfs_block_t root[2];
struct {
lfs_block_t begin;
@@ -135,6 +162,7 @@ typedef struct lfs {
uint32_t lookahead[LFS_CFG_LOOKAHEAD/32];
} lfs_t;
// Functions
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
@@ -160,4 +188,5 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
int lfs_deorphan(lfs_t *lfs);
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
#endif

View File

@@ -1,59 +0,0 @@
/*
* Block device interface
*
* Copyright (c) 2017 Christopher Haster
* Distributed under the MIT license
*/
#ifndef LFS_BD_H
#define LFS_BD_H
#include "lfs_config.h"
// Opaque type for block devices
typedef void lfs_bd_t;
// Description of block devices
struct lfs_bd_info {
lfs_size_t read_size; // Size of readable block
lfs_size_t prog_size; // Size of programmable block
lfs_size_t erase_size; // Size of erase block
uint64_t total_size; // Total size of the device
};
// Block device operations
//
// The little file system takes in a pointer to an opaque type
// and this struct, all operations are passed the opaque pointer
// which can be used to reference any state associated with the
// block device
struct lfs_bd_ops {
// Read a block
int (*read)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, void *buffer);
// Program a block
//
// The block must have previously been erased.
int (*prog)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size, const void *buffer);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int (*erase)(lfs_bd_t *bd, lfs_block_t block,
lfs_off_t off, lfs_size_t size);
// Sync the block device
int (*sync)(lfs_bd_t *bd);
// Get a description of the block device
//
// Any unknown information may be left as zero
int (*info)(lfs_bd_t *bd, struct lfs_bd_info *info);
};
#endif

View File

@@ -7,12 +7,12 @@ import os
import re
def main():
with open('blocks/info') as file:
s = struct.unpack('<LLL4xQ', file.read())
with open('blocks/config') as file:
s = struct.unpack('<LLLL', file.read())
print 'read_size: %d' % s[0]
print 'prog_size: %d' % s[1]
print 'erase_size: %d' % s[2]
print 'total_size: %d' % s[3]
print 'block_size: %d' % s[2]
print 'block_size: %d' % s[3]
print 'real_size: %d' % sum(
os.path.getsize(os.path.join('blocks', f))

View File

@@ -43,8 +43,6 @@ lfs_t lfs;
lfs_emubd_t bd;
lfs_file_t file[4];
lfs_dir_t dir[4];
struct lfs_bd_info bd_info;
struct lfs_bd_stats bd_stats;
struct lfs_info info;
uint8_t buffer[1024];
@@ -56,17 +54,41 @@ lfs_size_t rsize;
uintmax_t res;
const struct lfs_config config = {{
.bd = &bd,
.bd_ops = &lfs_emubd_ops,
#ifndef LFS_READ_SIZE
#define LFS_READ_SIZE 1
#endif
#ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE 1
#endif
#ifndef LFS_BLOCK_SIZE
#define LFS_BLOCK_SIZE 512
#endif
#ifndef LFS_BLOCK_COUNT
#define LFS_BLOCK_COUNT 1024
#endif
const struct lfs_config cfg = {{
.context = &bd,
.read = &lfs_emubd_read,
.prog = &lfs_emubd_prog,
.erase = &lfs_emubd_erase,
.sync = &lfs_emubd_sync,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
}};
// Entry point
int main() {{
lfs_emubd_create(&bd, "blocks");
lfs_emubd_create(&cfg, "blocks");
{tests}
lfs_emubd_destroy(&bd);
lfs_emubd_destroy(&cfg);
}}

View File

@@ -4,14 +4,14 @@ set -eu
echo "=== Allocator tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
SIZE=15000
lfs_mkdir() {
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "$1") => 0;
lfs_unmount(&lfs) => 0;
TEST
@@ -19,7 +19,7 @@ TEST
lfs_remove() {
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "$1/eggs") => 0;
lfs_remove(&lfs, "$1/bacon") => 0;
lfs_remove(&lfs, "$1/pancakes") => 0;
@@ -31,7 +31,7 @@ TEST
lfs_alloc_singleproc() {
tests/test.py << TEST
const char *names[] = {"bacon", "eggs", "pancakes"};
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
sprintf((char*)buffer, "$1/%s", names[n]);
lfs_file_open(&lfs, &file[n], (char*)buffer,
@@ -54,7 +54,7 @@ lfs_alloc_multiproc() {
for name in bacon eggs pancakes
do
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("$name");
@@ -72,7 +72,7 @@ lfs_verify() {
for name in bacon eggs pancakes
do
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0;
size = strlen("$name");
for (int i = 0; i < $SIZE; i++) {

View File

@@ -6,12 +6,12 @@ LARGESIZE=128
echo "=== Directory tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Root directory ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
@@ -19,14 +19,14 @@ TEST
echo "--- Directory creation ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- File creation ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
@@ -34,7 +34,7 @@ TEST
echo "--- Directory iteration ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -55,7 +55,7 @@ TEST
echo "--- Directory failures ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => LFS_ERROR_EXISTS;
lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERROR_NO_ENTRY;
lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERROR_NOT_DIR;
@@ -66,14 +66,14 @@ TEST
echo "--- Nested directories ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato/baked") => 0;
lfs_mkdir(&lfs, "potato/sweet") => 0;
lfs_mkdir(&lfs, "potato/fried") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "potato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -97,7 +97,7 @@ TEST
echo "--- Multi-block directory ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "cactus") => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i);
@@ -106,7 +106,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -125,7 +125,7 @@ TEST
echo "--- Directory remove ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "potato") => LFS_ERROR_INVALID;
lfs_remove(&lfs, "potato/sweet") => 0;
lfs_remove(&lfs, "potato/baked") => 0;
@@ -161,7 +161,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -182,7 +182,7 @@ TEST
echo "--- Directory rename ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_mkdir(&lfs, "coldpotato/baked") => 0;
lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
@@ -190,12 +190,12 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -217,7 +217,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "warmpotato") => 0;
lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERROR_INVALID;
@@ -228,7 +228,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
@@ -250,7 +250,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
@@ -260,7 +260,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;

View File

@@ -8,12 +8,12 @@ LARGESIZE=262144
echo "=== File tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Simple file test ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDWR | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("Hello World!\n");
memcpy(wbuffer, "Hello World!\n", size);
@@ -29,7 +29,7 @@ tests/test.py << TEST
lfs_size_t size = $1;
lfs_size_t chunk = 31;
srand(0);
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$2", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i;
@@ -48,7 +48,7 @@ tests/test.py << TEST
lfs_size_t size = $1;
lfs_size_t chunk = 29;
srand(0);
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$2", LFS_O_RDONLY) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i;
@@ -81,7 +81,7 @@ r_test $LARGESIZE largeavacado
echo "--- Dir check ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;

View File

@@ -6,42 +6,42 @@ rm -rf blocks
echo "--- Basic formatting ---"
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/null blocks/0
ln -f -s /dev/null blocks/1
tests/test.py << TEST
lfs_format(&lfs, &config) => LFS_ERROR_CORRUPT;
lfs_format(&lfs, &cfg) => LFS_ERROR_CORRUPT;
TEST
rm blocks/0 blocks/1
echo "--- Basic mounting ---"
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Invalid mount ---"
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
rm blocks/0 blocks/1
tests/test.py << TEST
lfs_mount(&lfs, &config) => LFS_ERROR_CORRUPT;
lfs_mount(&lfs, &cfg) => LFS_ERROR_CORRUPT;
TEST
echo "--- Valid corrupt mount ---"
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
rm blocks/0
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -4,12 +4,12 @@ set -eu
echo "=== Orphan tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Orphan test ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "parent") => 0;
lfs_mkdir(&lfs, "parent/orphan") => 0;
lfs_mkdir(&lfs, "parent/child") => 0;
@@ -19,7 +19,7 @@ TEST
# linked-list entry and should orphan the child
rm -v blocks/8
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERROR_NO_ENTRY;
unsigned before = 0;
lfs_traverse(&lfs, test_count, &before) => 0;

View File

@@ -4,11 +4,11 @@ set -eu
echo "=== Path tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &config) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "soda") => 0;
@@ -26,7 +26,7 @@ TEST
echo "--- Root path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
@@ -36,7 +36,7 @@ TEST
echo "--- Redundant slash path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
@@ -48,7 +48,7 @@ TEST
echo "--- Dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
@@ -62,7 +62,7 @@ TEST
echo "--- Dot dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
@@ -76,7 +76,7 @@ TEST
echo "--- Root dot dot path tests ---"
tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs_unmount(&lfs) => 0;