diff options
-rw-r--r-- | emubd/lfs_emubd.c | 218 | ||||
-rw-r--r-- | emubd/lfs_emubd.h | 51 | ||||
-rw-r--r-- | lfs.c | 141 | ||||
-rw-r--r-- | lfs.h | 51 | ||||
-rw-r--r-- | lfs_bd.h | 59 | ||||
-rwxr-xr-x | tests/stats.py | 8 | ||||
-rw-r--r-- | tests/template.fmt | 36 | ||||
-rwxr-xr-x | tests/test_alloc.sh | 12 | ||||
-rwxr-xr-x | tests/test_dirs.sh | 38 | ||||
-rwxr-xr-x | tests/test_files.sh | 10 | ||||
-rwxr-xr-x | tests/test_format.sh | 16 | ||||
-rwxr-xr-x | tests/test_orphan.sh | 6 | ||||
-rwxr-xr-x | tests/test_paths.sh | 14 |
13 files changed, 249 insertions, 411 deletions
diff --git a/emubd/lfs_emubd.c b/emubd/lfs_emubd.c index 7acd8f7..f1381cf 100644 --- a/emubd/lfs_emubd.c +++ b/emubd/lfs_emubd.c @@ -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,153 +64,131 @@ 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; + } - FILE *f = fopen(emu->path, "rb"); - if (!f && errno != ENOENT) { + if (f) { + int err = fseek(f, off, SEEK_SET); + if (err) { 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_t res = fread(data, 1, size, f); + if (res < size && !feof(f)) { + return -errno; } - size -= count; - data += count; - block += 1; - off = 0; + err = fclose(f); + if (err) { + return -errno; + } } emu->stats.read_count += 1; return 0; } -int lfs_emubd_prog(lfs_emubd_t *emu, 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) { + lfs_emubd_t *emu = cfg->context; 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; - } - } + assert(off % cfg->prog_size == 0); + assert(size % cfg->prog_size == 0); + assert(block < cfg->block_count); - int err = fseek(f, off, SEEK_SET); - if (err) { - return -errno; - } + // Program data + snprintf(emu->child, LFS_NAME_MAX, "%x", block); - size_t res = fwrite(data, 1, count, f); - if (res < count) { + FILE *f = fopen(emu->path, "r+b"); + if (!f && errno == ENOENT) { + f = fopen(emu->path, "w+b"); + if (!f) { return -errno; } + } - err = fclose(f); - if (err) { - 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; + } - size -= count; - data += count; - block += 1; - off = 0; + 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); - - // 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) { - return -errno; - } + assert(block < cfg->block_count); - if (!err && S_ISREG(st.st_mode)) { - int err = unlink(emu->path); - if (err) { - return -errno; - } - } + // 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; + } - size -= emu->info.erase_size; - block += 1; - off = 0; + if (!err && S_ISREG(st.st_mode)) { + int err = unlink(emu->path); + if (err) { + return -errno; + } } 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, -}; - diff --git a/emubd/lfs_emubd.h b/emubd/lfs_emubd.h index 89ee4ce..838dbd2 100644 --- a/emubd/lfs_emubd.h +++ b/emubd/lfs_emubd.h @@ -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 @@ -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; @@ -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 diff --git a/lfs_bd.h b/lfs_bd.h deleted file mode 100644 index 21d62ef..0000000 --- a/lfs_bd.h +++ /dev/null @@ -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 diff --git a/tests/stats.py b/tests/stats.py index b0c66a3..2ba1fb6 100755 --- a/tests/stats.py +++ b/tests/stats.py @@ -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)) diff --git a/tests/template.fmt b/tests/template.fmt index c7668db..8d42514 100644 --- a/tests/template.fmt +++ b/tests/template.fmt @@ -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); }} diff --git a/tests/test_alloc.sh b/tests/test_alloc.sh index 9604edd..48ff72a 100755 --- a/tests/test_alloc.sh +++ b/tests/test_alloc.sh @@ -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++) { diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index 6d73e7a..f47987a 100755 --- a/tests/test_dirs.sh +++ b/tests/test_dirs.sh @@ -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; diff --git a/tests/test_files.sh b/tests/test_files.sh index 7a381ec..dbdd923 100755 --- a/tests/test_files.sh +++ b/tests/test_files.sh @@ -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; diff --git a/tests/test_format.sh b/tests/test_format.sh index 0b53dc5..44674c8 100755 --- a/tests/test_format.sh +++ b/tests/test_format.sh @@ -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 diff --git a/tests/test_orphan.sh b/tests/test_orphan.sh index f923308..2a9b3f3 100755 --- a/tests/test_orphan.sh +++ b/tests/test_orphan.sh @@ -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; diff --git a/tests/test_paths.sh b/tests/test_paths.sh index b77fb94..769f37f 100755 --- a/tests/test_paths.sh +++ b/tests/test_paths.sh @@ -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; |