Changed callbacks to take user-provided context directly

This is a style change to make littlefs's callbacks consistent with most
callback declarations found in C. That is, taking in a user-provided
`void*`.

Previously, these callbacks took a pointer to the config struct itself,
which indirectly contained a user provided context, and this gets the
job done, but taking in a callback with a `void*` is arguably more
expected, has a better chance of integrating with C++/OS-specific code,
and is more likely to be optimized out by a clever compiler.

---

As a part of these changes, the geometry for the test bds needed to be
moved into bd specific configuration objects. This is a good change as
it also allows for testing situations where littlefs's geometry does not
match the underlying bd.
This commit is contained in:
Christopher Haster
2020-11-26 09:16:20 -06:00
parent a549413077
commit a7cdd563f6
15 changed files with 383 additions and 397 deletions

View File

@@ -10,21 +10,18 @@
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path, int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path,
const struct lfs_filebd_cfg *bdcfg) { const struct lfs_filebd_cfg *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p {.context=%p, " LFS_FILEBD_TRACE("lfs_filebd_createcfg(%p, \"%s\", %p {"
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
"\"%s\", " ".erase_value=%"PRId32"})",
"%p {.erase_value=%"PRId32"})", (void*)bd, path, (void*)cfg,
(void*)cfg, cfg->context, cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, cfg->erase_value);
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, // copy over config
path, (void*)bdcfg, bdcfg->erase_value); bd->cfg = *cfg;
lfs_filebd_t *bd = cfg->context;
bd->cfg = bdcfg;
// open file // open file
bd->fd = open(path, O_RDWR | O_CREAT, 0666); bd->fd = open(path, O_RDWR | O_CREAT, 0666);
@@ -38,26 +35,8 @@ int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path,
return 0; return 0;
} }
int lfs_filebd_create(const struct lfs_cfg *cfg, const char *path) { int lfs_filebd_destroy(lfs_filebd_t *bd) {
LFS_FILEBD_TRACE("lfs_filebd_create(%p {.context=%p, " LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)bd);
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\")",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path);
static const struct lfs_filebd_cfg defaults = {.erase_value=-1};
int err = lfs_filebd_createcfg(cfg, path, &defaults);
LFS_FILEBD_TRACE("lfs_filebd_create -> %d", err);
return err;
}
int lfs_filebd_destroy(const struct lfs_cfg *cfg) {
LFS_FILEBD_TRACE("lfs_filebd_destroy(%p)", (void*)cfg);
lfs_filebd_t *bd = cfg->context;
int err = close(bd->fd); int err = close(bd->fd);
if (err < 0) { if (err < 0) {
err = -errno; err = -errno;
@@ -68,26 +47,25 @@ int lfs_filebd_destroy(const struct lfs_cfg *cfg) {
return 0; return 0;
} }
int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_read(%p, " LFS_FILEBD_TRACE("lfs_filebd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)bd, block, off, buffer, size);
lfs_filebd_t *bd = cfg->context;
// check if read is valid // check if read is valid
LFS_ASSERT(off % cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// zero for reproducability (in case file is truncated) // zero for reproducability (in case file is truncated)
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(buffer, bd->cfg->erase_value, size); memset(buffer, bd->cfg.erase_value, size);
} }
// read // read
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_read -> %d", err);
@@ -105,21 +83,21 @@ int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block,
return 0; return 0;
} }
int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_FILEBD_TRACE("lfs_filebd_prog(%p, 0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", LFS_FILEBD_TRACE("lfs_filebd_prog(%p, "
(void*)cfg, block, off, buffer, size); "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
lfs_filebd_t *bd = cfg->context; (void*)bd, block, off, buffer, size);
// check if write is valid // check if write is valid
LFS_ASSERT(off % cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing // check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
@@ -135,13 +113,13 @@ int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block,
return err; return err;
} }
LFS_ASSERT(c == bd->cfg->erase_value); LFS_ASSERT(c == bd->cfg.erase_value);
} }
} }
// program data // program data
off_t res1 = lseek(bd->fd, off_t res1 = lseek(bd->fd,
(off_t)block*cfg->block_size + (off_t)off, SEEK_SET); (off_t)block*bd->cfg.erase_size + (off_t)off, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_prog -> %d", err);
@@ -159,24 +137,23 @@ int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block,
return 0; return 0;
} }
int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block) {
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
lfs_filebd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing // erase, only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
off_t res1 = lseek(bd->fd, (off_t)block*cfg->block_size, SEEK_SET); off_t res1 = lseek(bd->fd, (off_t)block*bd->cfg.erase_size, SEEK_SET);
if (res1 < 0) { if (res1 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
return err; return err;
} }
for (lfs_off_t i = 0; i < cfg->block_size; i++) { for (lfs_off_t i = 0; i < bd->cfg.erase_size; i++) {
ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg->erase_value}, 1); ssize_t res2 = write(bd->fd, &(uint8_t){bd->cfg.erase_value}, 1);
if (res2 < 0) { if (res2 < 0) {
int err = -errno; int err = -errno;
LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err); LFS_FILEBD_TRACE("lfs_filebd_erase -> %d", err);
@@ -189,10 +166,10 @@ int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block) {
return 0; return 0;
} }
int lfs_filebd_sync(const struct lfs_cfg *cfg) { int lfs_filebd_sync(lfs_filebd_t *bd) {
LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)cfg); LFS_FILEBD_TRACE("lfs_filebd_sync(%p)", (void*)bd);
// file sync // file sync
lfs_filebd_t *bd = cfg->context;
int err = fsync(bd->fd); int err = fsync(bd->fd);
if (err) { if (err) {
err = -errno; err = -errno;

View File

@@ -10,8 +10,7 @@
#include "lfs.h" #include "lfs.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
@@ -24,6 +23,20 @@ extern "C"
// filebd config (optional) // filebd config (optional)
struct lfs_filebd_cfg { struct lfs_filebd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate // 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding all the extra block-device // erases, which can speed up testing by avoiding all the extra block-device
// operations to store the erase value. // operations to store the erase value.
@@ -33,40 +46,39 @@ struct lfs_filebd_cfg {
// filebd state // filebd state
typedef struct lfs_filebd { typedef struct lfs_filebd {
int fd; int fd;
const struct lfs_filebd_cfg *cfg; struct lfs_filebd_cfg cfg;
} lfs_filebd_t; } lfs_filebd_t;
// Create a file block device using the geometry in lfs_cfg // Create a file block device using the geometry in lfs_filebd_cfg
int lfs_filebd_create(const struct lfs_cfg *cfg, const char *path); int lfs_filebd_createcfg(lfs_filebd_t *bd, const char *path,
int lfs_filebd_createcfg(const struct lfs_cfg *cfg, const char *path, const struct lfs_filebd_cfg *cfg);
const struct lfs_filebd_cfg *bdcfg);
// Clean up memory associated with block device // Clean up memory associated with block device
int lfs_filebd_destroy(const struct lfs_cfg *cfg); int lfs_filebd_destroy(lfs_filebd_t *bd);
// Read a block // Read a block
int lfs_filebd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_filebd_read(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block // Program a block
// //
// The block must have previously been erased. // The block must have previously been erased.
int lfs_filebd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_filebd_prog(lfs_filebd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block // Erase a block
// //
// A block must be erased before being programmed. The // A block must be erased before being programmed. The
// state of an erased block is undefined. // state of an erased block is undefined.
int lfs_filebd_erase(const struct lfs_cfg *cfg, lfs_block_t block); int lfs_filebd_erase(lfs_filebd_t *bd, lfs_block_t block);
// Sync the block device // Sync the block device
int lfs_filebd_sync(const struct lfs_cfg *cfg); int lfs_filebd_sync(lfs_filebd_t *bd);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ }
#endif #endif
#endif #endif

View File

@@ -6,26 +6,24 @@
*/ */
#include "bd/lfs_rambd.h" #include "bd/lfs_rambd.h"
int lfs_rambd_createcfg(const struct lfs_cfg *cfg, int lfs_rambd_createcfg(lfs_rambd_t *bd,
const struct lfs_rambd_cfg *bdcfg) { const struct lfs_rambd_cfg *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg(%p {.context=%p, " LFS_RAMBD_TRACE("lfs_filebd_createcfg(%p, %p {"
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
"%p {.erase_value=%"PRId32", .buffer=%p})", ".erase_value=%"PRId32", .buffer=%p})",
(void*)cfg, cfg->context, (void*)bd, (void*)cfg,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, cfg->erase_value, cfg->buffer);
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
(void*)bdcfg, bdcfg->erase_value, bdcfg->buffer); // copy over config
lfs_rambd_t *bd = cfg->context; bd->cfg = *cfg;
bd->cfg = bdcfg;
// allocate buffer? // allocate buffer?
if (bd->cfg->buffer) { if (bd->cfg.buffer) {
bd->buffer = bd->cfg->buffer; bd->buffer = bd->cfg.buffer;
} else { } else {
bd->buffer = lfs_malloc(cfg->block_size * cfg->block_count); bd->buffer = lfs_malloc(bd->cfg.erase_size * bd->cfg.erase_count);
if (!bd->buffer) { if (!bd->buffer) {
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM); LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
@@ -33,108 +31,89 @@ int lfs_rambd_createcfg(const struct lfs_cfg *cfg,
} }
// zero for reproducability? // zero for reproducability?
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(bd->buffer, bd->cfg->erase_value, memset(bd->buffer, bd->cfg.erase_value,
cfg->block_size * cfg->block_count); bd->cfg.erase_size * bd->cfg.erase_count);
} }
LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_createcfg -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_create(const struct lfs_cfg *cfg) { int lfs_rambd_destroy(lfs_rambd_t *bd) {
LFS_RAMBD_TRACE("lfs_rambd_create(%p {.context=%p, " LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)bd);
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"})",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count);
static const struct lfs_rambd_cfg defaults = {.erase_value=-1};
int err = lfs_rambd_createcfg(cfg, &defaults);
LFS_RAMBD_TRACE("lfs_rambd_create -> %d", err);
return err;
}
int lfs_rambd_destroy(const struct lfs_cfg *cfg) {
LFS_RAMBD_TRACE("lfs_rambd_destroy(%p)", (void*)cfg);
// clean up memory // clean up memory
lfs_rambd_t *bd = cfg->context; if (!bd->cfg.buffer) {
if (!bd->cfg->buffer) {
lfs_free(bd->buffer); lfs_free(bd->buffer);
} }
LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_destroy -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_read(%p, " LFS_RAMBD_TRACE("lfs_rambd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)bd, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context;
// check if read is valid // check if read is valid
LFS_ASSERT(off % cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// read data // read data
memcpy(buffer, &bd->buffer[block*cfg->block_size + off], size); memcpy(buffer, &bd->buffer[block*bd->cfg.erase_size + off], size);
LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_read -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_RAMBD_TRACE("lfs_rambd_prog(%p, " LFS_RAMBD_TRACE("lfs_rambd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)bd, block, off, buffer, size);
lfs_rambd_t *bd = cfg->context;
// check if write is valid // check if write is valid
LFS_ASSERT(off % cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// check that data was erased? only needed for testing // check that data was erased? only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
for (lfs_off_t i = 0; i < size; i++) { for (lfs_off_t i = 0; i < size; i++) {
LFS_ASSERT(bd->buffer[block*cfg->block_size + off + i] == LFS_ASSERT(bd->buffer[block*bd->cfg.erase_size + off + i] ==
bd->cfg->erase_value); bd->cfg.erase_value);
} }
} }
// program data // program data
memcpy(&bd->buffer[block*cfg->block_size + off], buffer, size); memcpy(&bd->buffer[block*bd->cfg.erase_size + off], buffer, size);
LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_prog -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block) {
LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_RAMBD_TRACE("lfs_rambd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
lfs_rambd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// erase, only needed for testing // erase, only needed for testing
if (bd->cfg->erase_value != -1) { if (bd->cfg.erase_value != -1) {
memset(&bd->buffer[block*cfg->block_size], memset(&bd->buffer[block*bd->cfg.erase_size],
bd->cfg->erase_value, cfg->block_size); bd->cfg.erase_value, bd->cfg.erase_size);
} }
LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_erase -> %d", 0);
return 0; return 0;
} }
int lfs_rambd_sync(const struct lfs_cfg *cfg) { int lfs_rambd_sync(lfs_rambd_t *bd) {
LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)cfg); LFS_RAMBD_TRACE("lfs_rambd_sync(%p)", (void*)bd);
// sync does nothing because we aren't backed by anything real // sync does nothing because we aren't backed by anything real
(void)cfg; (void)bd;
LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0); LFS_RAMBD_TRACE("lfs_rambd_sync -> %d", 0);
return 0; return 0;
} }

View File

@@ -10,8 +10,7 @@
#include "lfs.h" #include "lfs.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
@@ -24,6 +23,20 @@ extern "C"
// rambd config (optional) // rambd config (optional)
struct lfs_rambd_cfg { struct lfs_rambd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to simulate erasing with. -1 indicates no erase // 8-bit erase value to simulate erasing with. -1 indicates no erase
// occurs, which is still a valid block device // occurs, which is still a valid block device
int32_t erase_value; int32_t erase_value;
@@ -35,40 +48,39 @@ struct lfs_rambd_cfg {
// rambd state // rambd state
typedef struct lfs_rambd { typedef struct lfs_rambd {
uint8_t *buffer; uint8_t *buffer;
const struct lfs_rambd_cfg *cfg; struct lfs_rambd_cfg cfg;
} lfs_rambd_t; } lfs_rambd_t;
// Create a RAM block device using the geometry in lfs_cfg // Create a RAM block device using the geometry in lfs_cfg
int lfs_rambd_create(const struct lfs_cfg *cfg); int lfs_rambd_createcfg(lfs_rambd_t *bd,
int lfs_rambd_createcfg(const struct lfs_cfg *cfg, const struct lfs_rambd_cfg *cfg);
const struct lfs_rambd_cfg *bdcfg);
// Clean up memory associated with block device // Clean up memory associated with block device
int lfs_rambd_destroy(const struct lfs_cfg *cfg); int lfs_rambd_destroy(lfs_rambd_t *bd);
// Read a block // Read a block
int lfs_rambd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_rambd_read(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block // Program a block
// //
// The block must have previously been erased. // The block must have previously been erased.
int lfs_rambd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_rambd_prog(lfs_rambd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block // Erase a block
// //
// A block must be erased before being programmed. The // A block must be erased before being programmed. The
// state of an erased block is undefined. // state of an erased block is undefined.
int lfs_rambd_erase(const struct lfs_cfg *cfg, lfs_block_t block); int lfs_rambd_erase(lfs_rambd_t *bd, lfs_block_t block);
// Sync the block device // Sync the block device
int lfs_rambd_sync(const struct lfs_cfg *cfg); int lfs_rambd_sync(lfs_rambd_t *bd);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ }
#endif #endif
#endif #endif

View File

@@ -10,185 +10,164 @@
#include <stdlib.h> #include <stdlib.h>
int lfs_testbd_createcfg(const struct lfs_cfg *cfg, const char *path, int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
const struct lfs_testbd_cfg *bdcfg) { const struct lfs_testbd_cfg *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p {.context=%p, " LFS_TESTBD_TRACE("lfs_testbd_createcfg(%p, \"%s\", %p {"
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, " ".erase_size=%"PRIu32", .erase_count=%"PRIu32", "
"\"%s\", " ".erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
"%p {.erase_value=%"PRId32", .erase_cycles=%"PRIu32", "
".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", " ".badblock_behavior=%"PRIu8", .power_cycles=%"PRIu32", "
".buffer=%p, .wear_buffer=%p})", ".buffer=%p, .wear_buffer=%p})",
(void*)cfg, cfg->context, (void*)bd, path, (void*)cfg,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, cfg->read_size, cfg->prog_size, cfg->erase_size, cfg->erase_count,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, cfg->erase_value, cfg->erase_cycles,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, cfg->badblock_behavior, cfg->power_cycles,
path, (void*)bdcfg, bdcfg->erase_value, bdcfg->erase_cycles, cfg->buffer, cfg->wear_buffer);
bdcfg->badblock_behavior, bdcfg->power_cycles,
bdcfg->buffer, bdcfg->wear_buffer); // copy over config
lfs_testbd_t *bd = cfg->context; bd->cfg = *cfg;
bd->cfg = bdcfg;
// setup testing things // setup testing things
bd->persist = path; bd->persist = path;
bd->power_cycles = bd->cfg->power_cycles; bd->power_cycles = bd->cfg.power_cycles;
if (bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles) {
if (bd->cfg->wear_buffer) { if (bd->cfg.wear_buffer) {
bd->wear = bd->cfg->wear_buffer; bd->wear = bd->cfg.wear_buffer;
} else { } else {
bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->block_count); bd->wear = lfs_malloc(sizeof(lfs_testbd_wear_t)*cfg->erase_count);
if (!bd->wear) { if (!bd->wear) {
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM); LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", LFS_ERR_NOMEM);
return LFS_ERR_NOMEM; return LFS_ERR_NOMEM;
} }
} }
memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * cfg->block_count); memset(bd->wear, 0, sizeof(lfs_testbd_wear_t) * bd->cfg.erase_count);
} }
// create underlying block device // create underlying block device
if (bd->persist) { if (bd->persist) {
bd->u.file.cfg = (struct lfs_filebd_cfg){ int err = lfs_filebd_createcfg(&bd->impl.filebd, path,
.erase_value = bd->cfg->erase_value, &(struct lfs_filebd_cfg){
}; .read_size=bd->cfg.read_size,
int err = lfs_filebd_createcfg(cfg, path, &bd->u.file.cfg); .prog_size=bd->cfg.prog_size,
.erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} else { } else {
bd->u.ram.cfg = (struct lfs_rambd_cfg){ int err = lfs_rambd_createcfg(&bd->impl.rambd,
.erase_value = bd->cfg->erase_value, &(struct lfs_rambd_cfg){
.buffer = bd->cfg->buffer, .read_size=bd->cfg.read_size,
}; .prog_size=bd->cfg.prog_size,
int err = lfs_rambd_createcfg(cfg, &bd->u.ram.cfg); .erase_size=bd->cfg.erase_size,
.erase_count=bd->cfg.erase_count,
.erase_value=bd->cfg.erase_value,
.buffer=bd->cfg.buffer});
LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_createcfg -> %d", err);
return err; return err;
} }
} }
int lfs_testbd_create(const struct lfs_cfg *cfg, const char *path) { int lfs_testbd_destroy(lfs_testbd_t *bd) {
LFS_TESTBD_TRACE("lfs_testbd_create(%p {.context=%p, " LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)bd);
".read=%p, .prog=%p, .erase=%p, .sync=%p, " if (bd->cfg.erase_cycles && !bd->cfg.wear_buffer) {
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32"}, "
"\"%s\")",
(void*)cfg, cfg->context,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
path);
static const struct lfs_testbd_cfg defaults = {.erase_value=-1};
int err = lfs_testbd_createcfg(cfg, path, &defaults);
LFS_TESTBD_TRACE("lfs_testbd_create -> %d", err);
return err;
}
int lfs_testbd_destroy(const struct lfs_cfg *cfg) {
LFS_TESTBD_TRACE("lfs_testbd_destroy(%p)", (void*)cfg);
lfs_testbd_t *bd = cfg->context;
if (bd->cfg->erase_cycles && !bd->cfg->wear_buffer) {
lfs_free(bd->wear); lfs_free(bd->wear);
} }
if (bd->persist) { if (bd->persist) {
int err = lfs_filebd_destroy(cfg); int err = lfs_filebd_destroy(&bd->impl.filebd);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
return err; return err;
} else { } else {
int err = lfs_rambd_destroy(cfg); int err = lfs_rambd_destroy(&bd->impl.rambd);
LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_destroy -> %d", err);
return err; return err;
} }
} }
/// Internal mapping to block devices /// /// Internal mapping to block devices ///
static int lfs_testbd_rawread(const struct lfs_cfg *cfg, lfs_block_t block, static int lfs_testbd_rawread(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
lfs_testbd_t *bd = cfg->context;
if (bd->persist) { if (bd->persist) {
return lfs_filebd_read(cfg, block, off, buffer, size); return lfs_filebd_read(&bd->impl.filebd, block, off, buffer, size);
} else { } else {
return lfs_rambd_read(cfg, block, off, buffer, size); return lfs_rambd_read(&bd->impl.rambd, block, off, buffer, size);
} }
} }
static int lfs_testbd_rawprog(const struct lfs_cfg *cfg, lfs_block_t block, static int lfs_testbd_rawprog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
lfs_testbd_t *bd = cfg->context;
if (bd->persist) { if (bd->persist) {
return lfs_filebd_prog(cfg, block, off, buffer, size); return lfs_filebd_prog(&bd->impl.filebd, block, off, buffer, size);
} else { } else {
return lfs_rambd_prog(cfg, block, off, buffer, size); return lfs_rambd_prog(&bd->impl.rambd, block, off, buffer, size);
} }
} }
static int lfs_testbd_rawerase(const struct lfs_cfg *cfg, static int lfs_testbd_rawerase(lfs_testbd_t *bd,
lfs_block_t block) { lfs_block_t block) {
lfs_testbd_t *bd = cfg->context;
if (bd->persist) { if (bd->persist) {
return lfs_filebd_erase(cfg, block); return lfs_filebd_erase(&bd->impl.filebd, block);
} else { } else {
return lfs_rambd_erase(cfg, block); return lfs_rambd_erase(&bd->impl.rambd, block);
} }
} }
static int lfs_testbd_rawsync(const struct lfs_cfg *cfg) { static int lfs_testbd_rawsync(lfs_testbd_t *bd) {
lfs_testbd_t *bd = cfg->context;
if (bd->persist) { if (bd->persist) {
return lfs_filebd_sync(cfg); return lfs_filebd_sync(&bd->impl.filebd);
} else { } else {
return lfs_rambd_sync(cfg); return lfs_rambd_sync(&bd->impl.rambd);
} }
} }
/// block device API /// /// block device API ///
int lfs_testbd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) { lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_read(%p, " LFS_TESTBD_TRACE("lfs_testbd_read(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)bd, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context;
// check if read is valid // check if read is valid
LFS_ASSERT(off % cfg->read_size == 0); LFS_ASSERT(off % bd->cfg.read_size == 0);
LFS_ASSERT(size % cfg->read_size == 0); LFS_ASSERT(size % bd->cfg.read_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles && if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles &&
bd->cfg->badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) { bd->cfg.badblock_behavior == LFS_TESTBD_BADBLOCK_READERROR) {
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_read -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
// read // read
int err = lfs_testbd_rawread(cfg, block, off, buffer, size); int err = lfs_testbd_rawread(bd, block, off, buffer, size);
LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_read -> %d", err);
return err; return err;
} }
int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) { lfs_off_t off, const void *buffer, lfs_size_t size) {
LFS_TESTBD_TRACE("lfs_testbd_prog(%p, " LFS_TESTBD_TRACE("lfs_testbd_prog(%p, "
"0x%"PRIx32", %"PRIu32", %p, %"PRIu32")", "0x%"PRIx32", %"PRIu32", %p, %"PRIu32")",
(void*)cfg, block, off, buffer, size); (void*)bd, block, off, buffer, size);
lfs_testbd_t *bd = cfg->context;
// check if write is valid // check if write is valid
LFS_ASSERT(off % cfg->prog_size == 0); LFS_ASSERT(off % bd->cfg.prog_size == 0);
LFS_ASSERT(size % cfg->prog_size == 0); LFS_ASSERT(size % bd->cfg.prog_size == 0);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles && bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles && bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGERROR) { LFS_TESTBD_BADBLOCK_PROGERROR) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_PROGNOOP || LFS_TESTBD_BADBLOCK_PROGNOOP ||
bd->cfg->badblock_behavior == bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0); LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", 0);
return 0; return 0;
@@ -196,7 +175,7 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block,
} }
// prog // prog
int err = lfs_testbd_rawprog(cfg, block, off, buffer, size); int err = lfs_testbd_rawprog(bd, block, off, buffer, size);
if (err) { if (err) {
LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_prog -> %d", err);
return err; return err;
@@ -207,7 +186,7 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block,
bd->power_cycles -= 1; bd->power_cycles -= 1;
if (bd->power_cycles == 0) { if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes // sync to make sure we persist the last changes
assert(lfs_testbd_rawsync(cfg) == 0); assert(lfs_testbd_rawsync(bd) == 0);
// simulate power loss // simulate power loss
exit(33); exit(33);
} }
@@ -217,21 +196,20 @@ int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block,
return 0; return 0;
} }
int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) { int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)cfg, block); LFS_TESTBD_TRACE("lfs_testbd_erase(%p, 0x%"PRIx32")", (void*)bd, block);
lfs_testbd_t *bd = cfg->context;
// check if erase is valid // check if erase is valid
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
// block bad? // block bad?
if (bd->cfg->erase_cycles) { if (bd->cfg.erase_cycles) {
if (bd->wear[block] >= bd->cfg->erase_cycles) { if (bd->wear[block] >= bd->cfg.erase_cycles) {
if (bd->cfg->badblock_behavior == if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASEERROR) { LFS_TESTBD_BADBLOCK_ERASEERROR) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT); LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", LFS_ERR_CORRUPT);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} else if (bd->cfg->badblock_behavior == } else if (bd->cfg.badblock_behavior ==
LFS_TESTBD_BADBLOCK_ERASENOOP) { LFS_TESTBD_BADBLOCK_ERASENOOP) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0); LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", 0);
return 0; return 0;
@@ -243,7 +221,7 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) {
} }
// erase // erase
int err = lfs_testbd_rawerase(cfg, block); int err = lfs_testbd_rawerase(bd, block);
if (err) { if (err) {
LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_erase -> %d", err);
return err; return err;
@@ -254,7 +232,7 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) {
bd->power_cycles -= 1; bd->power_cycles -= 1;
if (bd->power_cycles == 0) { if (bd->power_cycles == 0) {
// sync to make sure we persist the last changes // sync to make sure we persist the last changes
assert(lfs_testbd_rawsync(cfg) == 0); assert(lfs_testbd_rawsync(bd) == 0);
// simulate power loss // simulate power loss
exit(33); exit(33);
} }
@@ -264,36 +242,34 @@ int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block) {
return 0; return 0;
} }
int lfs_testbd_sync(const struct lfs_cfg *cfg) { int lfs_testbd_sync(lfs_testbd_t *bd) {
LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)cfg); LFS_TESTBD_TRACE("lfs_testbd_sync(%p)", (void*)bd);
int err = lfs_testbd_rawsync(cfg); int err = lfs_testbd_rawsync(bd);
LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err); LFS_TESTBD_TRACE("lfs_testbd_sync -> %d", err);
return err; return err;
} }
/// simulated wear operations /// /// simulated wear operations ///
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_cfg *cfg, lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd,
lfs_block_t block) { lfs_block_t block) {
LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)cfg, block); LFS_TESTBD_TRACE("lfs_testbd_getwear(%p, %"PRIu32")", (void*)bd, block);
lfs_testbd_t *bd = cfg->context;
// check if block is valid // check if block is valid
LFS_ASSERT(bd->cfg->erase_cycles); LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]); LFS_TESTBD_TRACE("lfs_testbd_getwear -> %"PRIu32, bd->wear[block]);
return bd->wear[block]; return bd->wear[block];
} }
int lfs_testbd_setwear(const struct lfs_cfg *cfg, int lfs_testbd_setwear(lfs_testbd_t *bd,
lfs_block_t block, lfs_testbd_wear_t wear) { lfs_block_t block, lfs_testbd_wear_t wear) {
LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)cfg, block); LFS_TESTBD_TRACE("lfs_testbd_setwear(%p, %"PRIu32")", (void*)bd, block);
lfs_testbd_t *bd = cfg->context;
// check if block is valid // check if block is valid
LFS_ASSERT(bd->cfg->erase_cycles); LFS_ASSERT(bd->cfg.erase_cycles);
LFS_ASSERT(block < cfg->block_count); LFS_ASSERT(block < bd->cfg.erase_count);
bd->wear[block] = wear; bd->wear[block] = wear;

View File

@@ -13,8 +13,7 @@
#include "bd/lfs_filebd.h" #include "bd/lfs_filebd.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
@@ -45,6 +44,20 @@ typedef int32_t lfs_testbd_swear_t;
// testbd config, this is required for testing // testbd config, this is required for testing
struct lfs_testbd_cfg { struct lfs_testbd_cfg {
// Minimum size of block read. All read operations must be a
// multiple of this value.
lfs_size_t read_size;
// Minimum size of block program. All program operations must be a
// multiple of this value.
lfs_size_t prog_size;
// Size of an erasable block.
lfs_size_t erase_size;
// Number of erasable blocks on the device.
lfs_size_t erase_count;
// 8-bit erase value to use for simulating erases. -1 does not simulate // 8-bit erase value to use for simulating erases. -1 does not simulate
// erases, which can speed up testing by avoiding all the extra block-device // erases, which can speed up testing by avoiding all the extra block-device
// operations to store the erase value. // operations to store the erase value.
@@ -71,21 +84,15 @@ struct lfs_testbd_cfg {
// testbd state // testbd state
typedef struct lfs_testbd { typedef struct lfs_testbd {
union { union {
struct { lfs_filebd_t filebd;
lfs_filebd_t bd; lfs_rambd_t rambd;
struct lfs_filebd_cfg cfg; } impl;
} file;
struct {
lfs_rambd_t bd;
struct lfs_rambd_cfg cfg;
} ram;
} u;
bool persist; bool persist;
uint32_t power_cycles; uint32_t power_cycles;
lfs_testbd_wear_t *wear; lfs_testbd_wear_t *wear;
const struct lfs_testbd_cfg *cfg; struct lfs_testbd_cfg cfg;
} lfs_testbd_t; } lfs_testbd_t;
@@ -95,46 +102,45 @@ typedef struct lfs_testbd {
// //
// Note that filebd is used if a path is provided, if path is NULL // Note that filebd is used if a path is provided, if path is NULL
// testbd will use rambd which can be much faster. // testbd will use rambd which can be much faster.
int lfs_testbd_create(const struct lfs_cfg *cfg, const char *path); int lfs_testbd_createcfg(lfs_testbd_t *bd, const char *path,
int lfs_testbd_createcfg(const struct lfs_cfg *cfg, const char *path, const struct lfs_testbd_cfg *cfg);
const struct lfs_testbd_cfg *bdcfg);
// Clean up memory associated with block device // Clean up memory associated with block device
int lfs_testbd_destroy(const struct lfs_cfg *cfg); int lfs_testbd_destroy(lfs_testbd_t *bd);
// Read a block // Read a block
int lfs_testbd_read(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_testbd_read(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block // Program a block
// //
// The block must have previously been erased. // The block must have previously been erased.
int lfs_testbd_prog(const struct lfs_cfg *cfg, lfs_block_t block, int lfs_testbd_prog(lfs_testbd_t *bd, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block // Erase a block
// //
// A block must be erased before being programmed. The // A block must be erased before being programmed. The
// state of an erased block is undefined. // state of an erased block is undefined.
int lfs_testbd_erase(const struct lfs_cfg *cfg, lfs_block_t block); int lfs_testbd_erase(lfs_testbd_t *bd, lfs_block_t block);
// Sync the block device // Sync the block device
int lfs_testbd_sync(const struct lfs_cfg *cfg); int lfs_testbd_sync(lfs_testbd_t *bd);
/// Additional extended API for driving test features /// /// Additional extended API for driving test features ///
// Get simulated wear on a given block // Get simulated wear on a given block
lfs_testbd_swear_t lfs_testbd_getwear(const struct lfs_cfg *cfg, lfs_testbd_swear_t lfs_testbd_getwear(lfs_testbd_t *bd,
lfs_block_t block); lfs_block_t block);
// Manually set simulated wear on a given block // Manually set simulated wear on a given block
int lfs_testbd_setwear(const struct lfs_cfg *cfg, int lfs_testbd_setwear(lfs_testbd_t *bd,
lfs_block_t block, lfs_testbd_wear_t wear); lfs_block_t block, lfs_testbd_wear_t wear);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ }
#endif #endif
#endif #endif

20
lfs.c
View File

@@ -34,13 +34,13 @@
#else #else
// direct config towards dynamic lfs_cfg struct // direct config towards dynamic lfs_cfg struct
#define LFS_CFG_READ(lfs, block, off, buffer, size) \ #define LFS_CFG_READ(lfs, block, off, buffer, size) \
lfs->cfg->read(lfs->cfg, block, off, buffer, size) lfs->cfg->read(lfs->cfg->ctx, block, off, buffer, size)
#define LFS_CFG_PROG(lfs, block, off, buffer, size) \ #define LFS_CFG_PROG(lfs, block, off, buffer, size) \
lfs->cfg->prog(lfs->cfg, block, off, buffer, size) lfs->cfg->prog(lfs->cfg->ctx, block, off, buffer, size)
#define LFS_CFG_ERASE(lfs, block) \ #define LFS_CFG_ERASE(lfs, block) \
lfs->cfg->erase(lfs->cfg, block) lfs->cfg->erase(lfs->cfg->ctx, block)
#define LFS_CFG_SYNC(lfs) \ #define LFS_CFG_SYNC(lfs) \
lfs->cfg->sync(lfs->cfg) lfs->cfg->sync(lfs->cfg->ctx)
#define LFS_CFG_READ_SIZE(lfs) lfs->cfg->read_size #define LFS_CFG_READ_SIZE(lfs) lfs->cfg->read_size
#define LFS_CFG_PROG_SIZE(lfs) lfs->cfg->prog_size #define LFS_CFG_PROG_SIZE(lfs) lfs->cfg->prog_size
#define LFS_CFG_BLOCK_SIZE(lfs) lfs->cfg->block_size #define LFS_CFG_BLOCK_SIZE(lfs) lfs->cfg->block_size
@@ -3732,7 +3732,7 @@ int lfs_format(lfs_t *lfs) {
#if !defined(LFS_STATICCFG) #if !defined(LFS_STATICCFG)
int lfs_formatcfg(lfs_t *lfs, const struct lfs_cfg *cfg) { int lfs_formatcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
LFS_TRACE("lfs_formatcfg(%p, %p {.context=%p, " LFS_TRACE("lfs_formatcfg(%p, %p {.ctx=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", "
@@ -3741,7 +3741,7 @@ int lfs_formatcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".prog_buffer=%p, .lookahead_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", " ".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})", ".attr_max=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->context, (void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
@@ -3893,7 +3893,7 @@ int lfs_mount(lfs_t *lfs) {
#if !defined(LFS_STATICCFG) #if !defined(LFS_STATICCFG)
int lfs_mountcfg(lfs_t *lfs, const struct lfs_cfg *cfg) { int lfs_mountcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
LFS_TRACE("lfs_mountcfg(%p, %p {.context=%p, " LFS_TRACE("lfs_mountcfg(%p, %p {.ctx=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", "
@@ -3902,7 +3902,7 @@ int lfs_mountcfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".prog_buffer=%p, .lookahead_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", " ".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})", ".attr_max=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->context, (void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
@@ -5009,7 +5009,7 @@ int lfs_migrate(lfs_t *lfs) {
#if !defined(LFS_STATICCFG) #if !defined(LFS_STATICCFG)
int lfs_migratecfg(lfs_t *lfs, const struct lfs_cfg *cfg) { int lfs_migratecfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
LFS_TRACE("lfs_migratecfg(%p, %p {.context=%p, " LFS_TRACE("lfs_migratecfg(%p, %p {.ctx=%p, "
".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
".block_size=%"PRIu32", .block_count=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", "
@@ -5018,7 +5018,7 @@ int lfs_migratecfg(lfs_t *lfs, const struct lfs_cfg *cfg) {
".prog_buffer=%p, .lookahead_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, "
".name_max=%"PRIu32", .file_max=%"PRIu32", " ".name_max=%"PRIu32", .file_max=%"PRIu32", "
".attr_max=%"PRIu32"})", ".attr_max=%"PRIu32"})",
(void*)lfs, (void*)cfg, cfg->context, (void*)lfs, (void*)cfg, cfg->ctx,
(void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
(void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,

19
lfs.h
View File

@@ -130,28 +130,28 @@ enum lfs_whence_flags {
struct lfs_cfg { struct lfs_cfg {
// Opaque user provided context that can be used to pass // Opaque user provided context that can be used to pass
// information to the block device operations // information to the block device operations
void *context; void *ctx;
// Read a region in a block. Negative error codes are propogated // Read a region in a block. Negative error codes are propogated
// to the user. // to the user.
int (*read)(const struct lfs_cfg *c, lfs_block_t block, int (*read)(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size); lfs_off_t off, void *buffer, lfs_size_t size);
// Program a region in a block. The block must have previously // Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user. // been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs_cfg *c, lfs_block_t block, int (*prog)(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size); lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block. A block must be erased before being programmed. // Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes // The state of an erased block is undefined. Negative error codes
// are propogated to the user. // are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad. // May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs_cfg *c, lfs_block_t block); int (*erase)(void *ctx, lfs_block_t block);
// Sync the state of the underlying block device. Negative error codes // Sync the state of the underlying block device. Negative error codes
// are propogated to the user. // are propogated to the user.
int (*sync)(const struct lfs_cfg *c); int (*sync)(void *ctx);
// Minimum size of a block read. All read operations will be a // Minimum size of a block read. All read operations will be a
// multiple of this value. // multiple of this value.
@@ -266,15 +266,6 @@ int lfs_sync(void);
#ifndef LFS_LOOKAHEAD_BUFFER #ifndef LFS_LOOKAHEAD_BUFFER
#define LFS_LOOKAHEAD_BUFFER NULL #define LFS_LOOKAHEAD_BUFFER NULL
#endif #endif
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 0
#endif
#ifndef LFS_FILE_MAX
#define LFS_FILE_MAX 0
#endif
#ifndef LFS_ATTR_MAX
#define LFS_ATTR_MAX 0
#endif
#endif #endif
#if !defined(LFS_FILE_STATICCFG) #if !defined(LFS_FILE_STATICCFG)

View File

@@ -34,13 +34,43 @@ $(foreach target,$(SRC),$(eval $(FLATTEN)))
%.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f) %.test: %.test.o $(foreach f,$(subst /,.,$(SRC:.c=.o)),%.$f)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@ $(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
""" """
GLOBALS = """ BEFORE_MAIN = """
const char *lfs_testbd_path;
uint32_t lfs_testbd_cycles;
int lfs_testbd_readctx(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
return lfs_testbd_read((lfs_testbd_t*)ctx, block, off, buffer, size);
}
int lfs_testbd_progctx(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
return lfs_testbd_prog((lfs_testbd_t*)ctx, block, off, buffer, size);
}
int lfs_testbd_erasectx(void *ctx, lfs_block_t block) {
return lfs_testbd_erase((lfs_testbd_t*)ctx, block);
}
int lfs_testbd_syncctx(void *ctx) {
return lfs_testbd_sync((lfs_testbd_t*)ctx);
}
"""
BEFORE_TESTS = """
//////////////// AUTOGENERATED TEST //////////////// //////////////// AUTOGENERATED TEST ////////////////
#include "lfs.h" #include "lfs.h"
#include "bd/lfs_testbd.h" #include "bd/lfs_testbd.h"
#include <stdio.h> #include <stdio.h>
extern const char *lfs_testbd_path; extern const char *lfs_testbd_path;
extern uint32_t lfs_testbd_cycles; extern uint32_t lfs_testbd_cycles;
extern int lfs_testbd_readctx(void *ctx, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
extern int lfs_testbd_progctx(void *ctx, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
extern int lfs_testbd_erasectx(void *ctx, lfs_block_t block);
extern int lfs_testbd_syncctx(void *ctx);
""" """
DEFINES = { DEFINES = {
'LFS_READ_SIZE': 16, 'LFS_READ_SIZE': 16,
@@ -67,11 +97,11 @@ PROLOGUE = """
__attribute__((unused)) int err; __attribute__((unused)) int err;
__attribute__((unused)) const struct lfs_cfg cfg = { __attribute__((unused)) const struct lfs_cfg cfg = {
.context = &bd, .ctx = &bd,
.read = lfs_testbd_read, .read = lfs_testbd_readctx,
.prog = lfs_testbd_prog, .prog = lfs_testbd_progctx,
.erase = lfs_testbd_erase, .erase = lfs_testbd_erasectx,
.sync = lfs_testbd_sync, .sync = lfs_testbd_syncctx,
.read_size = LFS_READ_SIZE, .read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE, .prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE, .block_size = LFS_BLOCK_SIZE,
@@ -82,17 +112,21 @@ PROLOGUE = """
}; };
__attribute__((unused)) const struct lfs_testbd_cfg bdcfg = { __attribute__((unused)) const struct lfs_testbd_cfg bdcfg = {
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.erase_size = LFS_BLOCK_SIZE,
.erase_count = LFS_BLOCK_COUNT,
.erase_value = LFS_ERASE_VALUE, .erase_value = LFS_ERASE_VALUE,
.erase_cycles = LFS_ERASE_CYCLES, .erase_cycles = LFS_ERASE_CYCLES,
.badblock_behavior = LFS_BADBLOCK_BEHAVIOR, .badblock_behavior = LFS_BADBLOCK_BEHAVIOR,
.power_cycles = lfs_testbd_cycles, .power_cycles = lfs_testbd_cycles,
}; };
lfs_testbd_createcfg(&cfg, lfs_testbd_path, &bdcfg) => 0; lfs_testbd_createcfg(&bd, lfs_testbd_path, &bdcfg) => 0;
""" """
EPILOGUE = """ EPILOGUE = """
// epilogue // epilogue
lfs_testbd_destroy(&cfg) => 0; lfs_testbd_destroy(&bd) => 0;
""" """
PASS = '\033[32m✓\033[0m' PASS = '\033[32m✓\033[0m'
FAIL = '\033[31m✗\033[0m' FAIL = '\033[31m✗\033[0m'
@@ -468,7 +502,7 @@ class TestSuite:
def build(self, **args): def build(self, **args):
# build test files # build test files
tf = open(self.path + '.test.c.t', 'w') tf = open(self.path + '.test.c.t', 'w')
tf.write(GLOBALS) tf.write(BEFORE_TESTS)
if self.code is not None: if self.code is not None:
tf.write('#line %d "%s"\n' % (self.code_lineno, self.path)) tf.write('#line %d "%s"\n' % (self.code_lineno, self.path))
tf.write(self.code) tf.write(self.code)
@@ -483,14 +517,13 @@ class TestSuite:
for line in f: for line in f:
tfs[case.in_].write(line) tfs[case.in_].write(line)
tfs[case.in_].write('\n') tfs[case.in_].write('\n')
tfs[case.in_].write(GLOBALS) tfs[case.in_].write(BEFORE_TESTS)
tfs[case.in_].write('\n') tfs[case.in_].write('\n')
case.build(tfs[case.in_], **args) case.build(tfs[case.in_], **args)
tf.write(BEFORE_MAIN)
tf.write('\n') tf.write('\n')
tf.write('const char *lfs_testbd_path;\n')
tf.write('uint32_t lfs_testbd_cycles;\n')
tf.write('int main(int argc, char **argv) {\n') tf.write('int main(int argc, char **argv) {\n')
tf.write(4*' '+'int case_ = (argc > 1) ? atoi(argv[1]) : 0;\n') tf.write(4*' '+'int case_ = (argc > 1) ? atoi(argv[1]) : 0;\n')
tf.write(4*' '+'int perm = (argc > 2) ? atoi(argv[2]) : 0;\n') tf.write(4*' '+'int perm = (argc > 2) ? atoi(argv[2]) : 0;\n')

View File

@@ -362,7 +362,7 @@ code = '''
// but mark the head of our file as a "bad block", this is force our // but mark the head of our file as a "bad block", this is force our
// scan to bail early // scan to bail early
lfs_testbd_setwear(&cfg, fileblock, 0xffffffff) => 0; lfs_testbd_setwear(&bd, fileblock, 0xffffffff) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp"); strcpy((char*)buffer, "chomp");
size = strlen("chomp"); size = strlen("chomp");
@@ -377,7 +377,7 @@ code = '''
// now reverse the "bad block" and try to write the file again until we // now reverse the "bad block" and try to write the file again until we
// run out of space // run out of space
lfs_testbd_setwear(&cfg, fileblock, 0) => 0; lfs_testbd_setwear(&bd, fileblock, 0) => 0;
lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0;
strcpy((char*)buffer, "chomp"); strcpy((char*)buffer, "chomp");
size = strlen("chomp"); size = strlen("chomp");

View File

@@ -16,8 +16,8 @@ define.NAMEMULT = 64
define.FILEMULT = 1 define.FILEMULT = 1
code = ''' code = '''
for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) { for (lfs_block_t badblock = 2; badblock < LFS_BLOCK_COUNT; badblock++) {
lfs_testbd_setwear(&cfg, badblock-1, 0) => 0; lfs_testbd_setwear(&bd, badblock-1, 0) => 0;
lfs_testbd_setwear(&cfg, badblock, 0xffffffff) => 0; lfs_testbd_setwear(&bd, badblock, 0xffffffff) => 0;
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
@@ -90,7 +90,7 @@ define.NAMEMULT = 64
define.FILEMULT = 1 define.FILEMULT = 1
code = ''' code = '''
for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(&cfg, i+2, 0xffffffff) => 0; lfs_testbd_setwear(&bd, i+2, 0xffffffff) => 0;
} }
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
@@ -163,7 +163,7 @@ define.NAMEMULT = 64
define.FILEMULT = 1 define.FILEMULT = 1
code = ''' code = '''
for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) { for (lfs_block_t i = 0; i < (LFS_BLOCK_COUNT-2)/2; i++) {
lfs_testbd_setwear(&cfg, (2*i) + 2, 0xffffffff) => 0; lfs_testbd_setwear(&bd, (2*i) + 2, 0xffffffff) => 0;
} }
lfs_formatcfg(&lfs, &cfg) => 0; lfs_formatcfg(&lfs, &cfg) => 0;
@@ -233,8 +233,8 @@ define.LFS_BADBLOCK_BEHAVIOR = [
'LFS_TESTBD_BADBLOCK_ERASENOOP', 'LFS_TESTBD_BADBLOCK_ERASENOOP',
] ]
code = ''' code = '''
lfs_testbd_setwear(&cfg, 0, 0xffffffff) => 0; lfs_testbd_setwear(&bd, 0, 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, 1, 0xffffffff) => 0; lfs_testbd_setwear(&bd, 1, 0xffffffff) => 0;
lfs_formatcfg(&lfs, &cfg) => LFS_ERR_NOSPC; lfs_formatcfg(&lfs, &cfg) => LFS_ERR_NOSPC;
lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT; lfs_mountcfg(&lfs, &cfg) => LFS_ERR_CORRUPT;

View File

@@ -158,12 +158,12 @@ code = '''
lfs_ctz_fromle32(&ctz); lfs_ctz_fromle32(&ctz);
// rewrite block to contain bad pointer // rewrite block to contain bad pointer
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
uint32_t bad = lfs_tole32(0xcccccccc); uint32_t bad = lfs_tole32(0xcccccccc);
memcpy(&bbuffer[0], &bad, sizeof(bad)); memcpy(&bbuffer[0], &bad, sizeof(bad));
memcpy(&bbuffer[4], &bad, sizeof(bad)); memcpy(&bbuffer[4], &bad, sizeof(bad));
cfg.erase(&cfg, ctz.head) => 0; lfs_testbd_erase(&bd, ctz.head) => 0;
cfg.prog(&cfg, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, ctz.head, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
lfs_deinit(&lfs) => 0; lfs_deinit(&lfs) => 0;
// test that accessing our bad file fails, note there's a number // test that accessing our bad file fails, note there's a number

View File

@@ -180,7 +180,7 @@ code = '''
for (int run = 0; run < 2; run++) { for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_setwear(&cfg, b, lfs_testbd_setwear(&bd, b,
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
} }
@@ -272,7 +272,7 @@ code = '''
for (int run = 0; run < 2; run++) { for (int run = 0; run < 2; run++) {
for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) { for (lfs_block_t b = 0; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_setwear(&cfg, b, lfs_testbd_setwear(&bd, b,
(b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0; (b < run_block_count[run]) ? 0 : LFS_ERASE_CYCLES) => 0;
} }
@@ -434,7 +434,7 @@ exhausted:
lfs_testbd_wear_t maxwear = 0; lfs_testbd_wear_t maxwear = 0;
// skip 0 and 1 as superblock movement is intentionally avoided // skip 0 and 1 as superblock movement is intentionally avoided
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); lfs_testbd_wear_t wear = lfs_testbd_getwear(&bd, b);
printf("%08x: wear %d\n", b, wear); printf("%08x: wear %d\n", b, wear);
assert(wear >= 0); assert(wear >= 0);
if (wear < minwear) { if (wear < minwear) {
@@ -453,7 +453,7 @@ exhausted:
// find standard deviation^2 // find standard deviation^2
lfs_testbd_wear_t dev2 = 0; lfs_testbd_wear_t dev2 = 0;
for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) {
lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); lfs_testbd_wear_t wear = lfs_testbd_getwear(&bd, b);
assert(wear >= 0); assert(wear >= 0);
lfs_testbd_swear_t diff = wear - avgwear; lfs_testbd_swear_t diff = wear - avgwear;
dev2 += diff*diff; dev2 += diff*diff;

View File

@@ -98,15 +98,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_open(&lfs, &dir, "a") => 0;
@@ -174,15 +174,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// corrupt the destination // corrupt the destination
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -190,15 +190,15 @@ code = '''
block = dir.m.pair[0]; block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
off = LFS_BLOCK_SIZE-1; off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_open(&lfs, &dir, "a") => 0;
@@ -266,15 +266,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// corrupt the destination // corrupt the destination
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -282,15 +282,15 @@ code = '''
block = dir.m.pair[0]; block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
off = LFS_BLOCK_SIZE-1; off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// continue move // continue move
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -536,15 +536,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_open(&lfs, &dir, "a") => 0;
@@ -620,15 +620,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// corrupt the destination // corrupt the destination
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -636,15 +636,15 @@ code = '''
block = dir.m.pair[0]; block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
off = LFS_BLOCK_SIZE-1; off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "a") => 0; lfs_dir_open(&lfs, &dir, "a") => 0;
@@ -720,15 +720,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// corrupt the destination // corrupt the destination
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -736,15 +736,15 @@ code = '''
block = dir.m.pair[0]; block = dir.m.pair[0];
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
off = LFS_BLOCK_SIZE-1; off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
// continue move // continue move
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
@@ -1569,14 +1569,14 @@ code = '''
// force specific directories to relocate // force specific directories to relocate
if (RELOCATIONS & 0x1) { if (RELOCATIONS & 0x1) {
lfs_dir_open(&lfs, &dir, "/parent"); lfs_dir_open(&lfs, &dir, "/parent");
lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
} }
if (RELOCATIONS & 0x2) { if (RELOCATIONS & 0x2) {
lfs_dir_open(&lfs, &dir, "/parent/child"); lfs_dir_open(&lfs, &dir, "/parent/child");
lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
} }
@@ -1707,20 +1707,20 @@ code = '''
// force specific directories to relocate // force specific directories to relocate
if (RELOCATIONS & 0x1) { if (RELOCATIONS & 0x1) {
lfs_dir_open(&lfs, &dir, "/parent"); lfs_dir_open(&lfs, &dir, "/parent");
lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
} }
if (RELOCATIONS & 0x2) { if (RELOCATIONS & 0x2) {
lfs_dir_open(&lfs, &dir, "/parent/sibling"); lfs_dir_open(&lfs, &dir, "/parent/sibling");
lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
} }
if (RELOCATIONS & 0x4) { if (RELOCATIONS & 0x4) {
lfs_dir_open(&lfs, &dir, "/parent/child"); lfs_dir_open(&lfs, &dir, "/parent/child");
lfs_testbd_setwear(&cfg, dir.m.pair[0], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[0], 0xffffffff) => 0;
lfs_testbd_setwear(&cfg, dir.m.pair[1], 0xffffffff) => 0; lfs_testbd_setwear(&bd, dir.m.pair[1], 0xffffffff) => 0;
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
} }

View File

@@ -19,15 +19,15 @@ code = '''
lfs_dir_close(&lfs, &dir) => 0; lfs_dir_close(&lfs, &dir) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
uint8_t bbuffer[LFS_BLOCK_SIZE]; uint8_t bbuffer[LFS_BLOCK_SIZE];
cfg.read(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_read(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
int off = LFS_BLOCK_SIZE-1; int off = LFS_BLOCK_SIZE-1;
while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) { while (off >= 0 && bbuffer[off] == LFS_ERASE_VALUE) {
off -= 1; off -= 1;
} }
memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3); memset(&bbuffer[off-3], LFS_BLOCK_SIZE, 3);
cfg.erase(&cfg, block) => 0; lfs_testbd_erase(&bd, block) => 0;
cfg.prog(&cfg, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0; lfs_testbd_prog(&bd, block, 0, bbuffer, LFS_BLOCK_SIZE) => 0;
cfg.sync(&cfg) => 0; lfs_testbd_sync(&bd) => 0;
lfs_mountcfg(&lfs, &cfg) => 0; lfs_mountcfg(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;