Initial commit of basic file creation
Currently limited to inlined files and only simpler truncate-writes. But still this lets us test file creation/deletion. This is also enough logic to make it clear that, even though we have some powerful high-level primitives, mapping file operations onto these is still going to be non-trivial.
This commit is contained in:
@@ -1000,32 +1000,6 @@ static lfs_ssize_t lfsr_bd_progtag(lfs_t *lfs,
|
|||||||
|
|
||||||
/// lfsr_data_t stuff ///
|
/// lfsr_data_t stuff ///
|
||||||
|
|
||||||
// either an on-disk or in-device data pointer
|
|
||||||
typedef struct lfsr_data {
|
|
||||||
union {
|
|
||||||
// The sign-bit of the size field indicates if the data is in-device
|
|
||||||
// or on-disk.
|
|
||||||
//
|
|
||||||
// After removing the sign bit, the size always encodes the resulting
|
|
||||||
// size on-disk.
|
|
||||||
//
|
|
||||||
lfs_ssize_t size;
|
|
||||||
struct {
|
|
||||||
lfs_ssize_t size;
|
|
||||||
// This leb128 field is a bit of a hack that allows a single leb128
|
|
||||||
// to be injected into lfsr_bd_progdata. Outside of
|
|
||||||
// lfsr_bd_progdata, this field is invalid!
|
|
||||||
int32_t leb128;
|
|
||||||
const uint8_t *buffer;
|
|
||||||
} b;
|
|
||||||
struct {
|
|
||||||
lfs_ssize_t size;
|
|
||||||
lfs_size_t off;
|
|
||||||
lfs_block_t block;
|
|
||||||
} d;
|
|
||||||
} u;
|
|
||||||
} lfsr_data_t;
|
|
||||||
|
|
||||||
// the most common use is to pass a buffer with explicit size
|
// the most common use is to pass a buffer with explicit size
|
||||||
#define LFSR_DATA(_buffer, _size) \
|
#define LFSR_DATA(_buffer, _size) \
|
||||||
((lfsr_data_t){ \
|
((lfsr_data_t){ \
|
||||||
@@ -1764,10 +1738,6 @@ static int lfsr_data_readgrm(lfs_t *lfs, lfsr_data_t *data,
|
|||||||
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block);
|
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block);
|
||||||
static void lfs_alloc_ack(lfs_t *lfs);
|
static void lfs_alloc_ack(lfs_t *lfs);
|
||||||
|
|
||||||
// and our main "fix everything before writing" function
|
|
||||||
static int lfsr_fs_preparemutation(lfs_t *lfs);
|
|
||||||
static int lfsr_fs_fixgrm(lfs_t *lfs);
|
|
||||||
|
|
||||||
|
|
||||||
/// Red-black-yellow Dhara tree operations ///
|
/// Red-black-yellow Dhara tree operations ///
|
||||||
|
|
||||||
@@ -6891,6 +6861,75 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Prepare the filesystem for mutation ///
|
||||||
|
|
||||||
|
static int lfsr_fs_fixgrm(lfs_t *lfs) {
|
||||||
|
while (lfsr_grm_hasrm(&lfs->grm)) {
|
||||||
|
// find our mdir
|
||||||
|
lfsr_mdir_t mdir;
|
||||||
|
LFS_ASSERT(lfs->grm.rms[0] < lfs_smax32(
|
||||||
|
lfsr_mtree_weight(lfs),
|
||||||
|
lfsr_mleafweight(lfs)));
|
||||||
|
int err = lfsr_mtree_lookup(lfs, lfs->grm.rms[0], &mdir);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark grm as taken care of
|
||||||
|
lfsr_grm_t grm = lfs->grm;
|
||||||
|
lfsr_grm_poprm(&grm);
|
||||||
|
|
||||||
|
// make sure to adjust any remaining grms
|
||||||
|
if ((grm.rms[0] & lfsr_midbmask(lfs))
|
||||||
|
== (mdir.mid & lfsr_midbmask(lfs))
|
||||||
|
&& grm.rms[0] >= mdir.mid) {
|
||||||
|
LFS_ASSERT(grm.rms[0] != mdir.mid);
|
||||||
|
grm.rms[0] -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the rid while also updating our grm
|
||||||
|
LFS_ASSERT((lfs->grm.rms[0] & lfsr_midrmask(lfs)) < mdir.u.m.weight);
|
||||||
|
err = lfsr_mdir_commit(lfs, &mdir, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(mdir.mid, RM, -1, NULL),
|
||||||
|
LFSR_ATTR(-1, GRM, 0, GRM(&grm))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lfsr_fs_preparemutation(lfs_t *lfs) {
|
||||||
|
// checkpoint the allocator
|
||||||
|
lfs_alloc_ack(lfs);
|
||||||
|
|
||||||
|
// fix pending grms
|
||||||
|
if (lfsr_grm_hasrm(&lfs->grm)) {
|
||||||
|
if (lfsr_grm_count(&lfs->grm) == 2) {
|
||||||
|
LFS_DEBUG("Fixing pending grm "
|
||||||
|
"%"PRId32".%"PRId32" %"PRId32".%"PRId32,
|
||||||
|
lfs->grm.rms[0] >> lfs->mleaf_bits,
|
||||||
|
lfs->grm.rms[0] & lfsr_midrmask(lfs),
|
||||||
|
lfs->grm.rms[1] >> lfs->mleaf_bits,
|
||||||
|
lfs->grm.rms[1] & lfsr_midrmask(lfs));
|
||||||
|
} else if (lfsr_grm_count(&lfs->grm) == 1) {
|
||||||
|
LFS_DEBUG("Fixing pending grm %"PRId32".%"PRId32,
|
||||||
|
lfs->grm.rms[0] >> lfs->mleaf_bits,
|
||||||
|
lfs->grm.rms[0] & lfsr_midrmask(lfs));
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = lfsr_fs_fixgrm(lfs);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkpoint the allocator again since our fixgrm completed some
|
||||||
|
// work
|
||||||
|
lfs_alloc_ack(lfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Directory operations ///
|
/// Directory operations ///
|
||||||
|
|
||||||
int lfsr_mkdir(lfs_t *lfs, const char *path) {
|
int lfsr_mkdir(lfs_t *lfs, const char *path) {
|
||||||
@@ -7238,9 +7277,66 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
|
|||||||
return lfsr_fs_fixgrm(lfs);
|
return lfsr_fs_fixgrm(lfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
// common stat function once we have an mdir
|
||||||
memset(info, 0, sizeof(struct lfs_info));
|
static int lfsr_mdir_stat(lfs_t *lfs, lfsr_mdir_t *mdir, lfsr_mid_t mid,
|
||||||
|
lfsr_sdid_t did, struct lfs_info *info) {
|
||||||
|
// lookup our name tag
|
||||||
|
lfsr_tag_t tag;
|
||||||
|
lfsr_data_t data;
|
||||||
|
int err = lfsr_mdir_lookup(lfs, mdir, mid, LFSR_TAG_WIDE(NAME),
|
||||||
|
&tag, &data);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get our did
|
||||||
|
lfsr_did_t did_;
|
||||||
|
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&did_);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// did mismatch? this terminates dir reads
|
||||||
|
if (did != -1 && did_ != (lfsr_did_t)did) {
|
||||||
|
return LFS_ERR_NOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get file type from the tag
|
||||||
|
info->type = lfsr_tag_filetype(tag);
|
||||||
|
|
||||||
|
// get file name from the name entry
|
||||||
|
LFS_ASSERT(lfsr_data_size(&data) <= LFS_NAME_MAX);
|
||||||
|
lfs_ssize_t name_size = lfsr_data_read(lfs, &data,
|
||||||
|
info->name, LFS_NAME_MAX);
|
||||||
|
if (name_size < 0) {
|
||||||
|
return name_size;
|
||||||
|
}
|
||||||
|
info->name[name_size] = '\0';
|
||||||
|
|
||||||
|
// get file size if we're a regular file, this gets a bit messy
|
||||||
|
// because of the different file representations
|
||||||
|
info->size = 0;
|
||||||
|
if (tag == LFSR_TAG_REG) {
|
||||||
|
err = lfsr_mdir_lookup(lfs, mdir, mid, LFSR_TAG_WIDE(STRUCT),
|
||||||
|
&tag, &data);
|
||||||
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != LFS_ERR_NOENT) {
|
||||||
|
if (tag == LFSR_TAG_INLINED) {
|
||||||
|
info->size = lfsr_data_size(&data);
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
LFS_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
||||||
// lookup our entry
|
// lookup our entry
|
||||||
lfsr_mdir_t mdir;
|
lfsr_mdir_t mdir;
|
||||||
lfsr_tag_t tag;
|
lfsr_tag_t tag;
|
||||||
@@ -7261,15 +7357,7 @@ int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fill out our info struct
|
// fill out our info struct
|
||||||
info->type = lfsr_tag_filetype(tag);
|
return lfsr_mdir_stat(lfs, &mdir, mdir.mid, -1, info);
|
||||||
|
|
||||||
LFS_ASSERT(name_size <= LFS_NAME_MAX);
|
|
||||||
memcpy(info->name, name, name_size);
|
|
||||||
info->name[name_size] = '\0';
|
|
||||||
|
|
||||||
// TODO size once we have actual files
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
||||||
@@ -7313,13 +7401,13 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add to tracked mdirs
|
// add to tracked mdirs
|
||||||
lfsr_mdir_addopened(lfs, LFS_TYPE_DIR, (lfsr_openedmdir_t*)dir);
|
lfsr_mdir_addopened(lfs, LFS_TYPE_DIR, &dir->m);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lfsr_dir_close(lfs_t *lfs, lfsr_dir_t *dir) {
|
int lfsr_dir_close(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||||
// remove from tracked mdirs
|
// remove from tracked mdirs
|
||||||
lfsr_mdir_removeopened(lfs, LFS_TYPE_DIR, (lfsr_openedmdir_t*)dir);
|
lfsr_mdir_removeopened(lfs, LFS_TYPE_DIR, &dir->m);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7348,42 +7436,14 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup our name tag
|
// fill out our info struct
|
||||||
lfsr_tag_t tag;
|
//
|
||||||
lfsr_data_t data;
|
// this will return LFS_ERR_NOENT if our dids mismatch
|
||||||
err = lfsr_mdir_lookup(lfs, &dir->m.mdir,
|
err = lfsr_mdir_stat(lfs, &dir->m.mdir, dir->m.mdir.mid, dir->did, info);
|
||||||
dir->m.mdir.mid, LFSR_TAG_WIDE(NAME),
|
|
||||||
&tag, &data);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get our did
|
|
||||||
lfsr_did_t did;
|
|
||||||
err = lfsr_data_readleb128(lfs, &data, (int32_t*)&did);
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// did mismatch? we must be done
|
|
||||||
if (did != dir->did) {
|
|
||||||
return LFS_ERR_NOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get file name from the name entry
|
|
||||||
LFS_ASSERT(lfsr_data_size(&data) <= LFS_NAME_MAX);
|
|
||||||
lfs_ssize_t name_size = lfsr_data_read(lfs, &data,
|
|
||||||
info->name, LFS_NAME_MAX);
|
|
||||||
if (name_size < 0) {
|
|
||||||
return name_size;
|
|
||||||
}
|
|
||||||
info->name[name_size] = '\0';
|
|
||||||
|
|
||||||
// get file type from the tag
|
|
||||||
info->type = lfsr_tag_filetype(tag);
|
|
||||||
|
|
||||||
// TODO get size once we actually have regular files
|
|
||||||
|
|
||||||
// eagerly look up the next entry
|
// eagerly look up the next entry
|
||||||
err = lfsr_mtree_seek(lfs, &dir->m.mdir, 1);
|
err = lfsr_mtree_seek(lfs, &dir->m.mdir, 1);
|
||||||
if (err && err != LFS_ERR_NOENT) {
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
@@ -7456,74 +7516,300 @@ int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Prepare the filesystem for mutation ///
|
/// File operations ///
|
||||||
|
|
||||||
static int lfsr_fs_fixgrm(lfs_t *lfs) {
|
static bool lfsr_file_isreadable(uint32_t flags) {
|
||||||
while (lfsr_grm_hasrm(&lfs->grm)) {
|
return (flags & LFS_O_RDONLY) == LFS_O_RDONLY;
|
||||||
// find our mdir
|
}
|
||||||
lfsr_mdir_t mdir;
|
|
||||||
LFS_ASSERT(lfs->grm.rms[0] < lfs_smax32(
|
static bool lfsr_file_iswriteable(uint32_t flags) {
|
||||||
lfsr_mtree_weight(lfs),
|
return (flags & LFS_O_WRONLY) == LFS_O_WRONLY;
|
||||||
lfsr_mleafweight(lfs)));
|
}
|
||||||
int err = lfsr_mtree_lookup(lfs, lfs->grm.rms[0], &mdir);
|
|
||||||
|
int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file);
|
||||||
|
|
||||||
|
int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const char *path, uint32_t flags,
|
||||||
|
const struct lfs_file_config *cfg) {
|
||||||
|
if (lfsr_file_iswriteable(flags)) {
|
||||||
|
// prepare our filesystem for writing
|
||||||
|
int err = lfsr_fs_preparemutation(lfs);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark grm as taken care of
|
|
||||||
lfsr_grm_t grm = lfs->grm;
|
|
||||||
lfsr_grm_poprm(&grm);
|
|
||||||
|
|
||||||
// make sure to adjust any remaining grms
|
|
||||||
if ((grm.rms[0] & lfsr_midbmask(lfs))
|
|
||||||
== (mdir.mid & lfsr_midbmask(lfs))
|
|
||||||
&& grm.rms[0] >= mdir.mid) {
|
|
||||||
LFS_ASSERT(grm.rms[0] != mdir.mid);
|
|
||||||
grm.rms[0] -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the rid while also updating our grm
|
|
||||||
LFS_ASSERT((lfs->grm.rms[0] & lfsr_midrmask(lfs)) < mdir.u.m.weight);
|
|
||||||
err = lfsr_mdir_commit(lfs, &mdir, LFSR_ATTRS(
|
|
||||||
LFSR_ATTR(mdir.mid, RM, -1, NULL),
|
|
||||||
LFSR_ATTR(-1, GRM, 0, GRM(&grm))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
// default inlined state
|
||||||
}
|
file->flags = flags;
|
||||||
|
file->cfg = cfg;
|
||||||
|
file->pos = 0;
|
||||||
|
file->inlined_pos = 0;
|
||||||
|
file->inlined = LFSR_DATA_NULL;
|
||||||
|
|
||||||
static int lfsr_fs_preparemutation(lfs_t *lfs) {
|
// lookup our parent
|
||||||
// checkpoint the allocator
|
lfsr_tag_t tag;
|
||||||
lfs_alloc_ack(lfs);
|
lfsr_did_t did;
|
||||||
|
const char *name;
|
||||||
|
lfs_size_t name_size;
|
||||||
|
int err = lfsr_mtree_pathlookup(lfs, path,
|
||||||
|
&file->m.mdir, &tag,
|
||||||
|
&did, &name, &name_size);
|
||||||
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// fix pending grms
|
// creating a new entry?
|
||||||
if (lfsr_grm_hasrm(&lfs->grm)) {
|
if (err == LFS_ERR_NOENT) {
|
||||||
if (lfsr_grm_count(&lfs->grm) == 2) {
|
if (!(flags & LFS_O_CREAT)) {
|
||||||
LFS_DEBUG("Fixing pending grm "
|
return LFS_ERR_NOENT;
|
||||||
"%"PRId32".%"PRId32" %"PRId32".%"PRId32,
|
}
|
||||||
lfs->grm.rms[0] >> lfs->mleaf_bits,
|
LFS_ASSERT(lfsr_file_iswriteable(flags));
|
||||||
lfs->grm.rms[0] & lfsr_midrmask(lfs),
|
|
||||||
lfs->grm.rms[1] >> lfs->mleaf_bits,
|
// check that name fits
|
||||||
lfs->grm.rms[1] & lfsr_midrmask(lfs));
|
if (name_size > lfs->name_limit) {
|
||||||
} else if (lfsr_grm_count(&lfs->grm) == 1) {
|
return LFS_ERR_NAMETOOLONG;
|
||||||
LFS_DEBUG("Fixing pending grm %"PRId32".%"PRId32,
|
|
||||||
lfs->grm.rms[0] >> lfs->mleaf_bits,
|
|
||||||
lfs->grm.rms[0] & lfsr_midrmask(lfs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = lfsr_fs_fixgrm(lfs);
|
// create our entry
|
||||||
|
//
|
||||||
|
// note this risks creating a zero-length file if we lose power here,
|
||||||
|
// but it's the only way for us to save the file name.
|
||||||
|
//
|
||||||
|
// TODO or is it? ;)
|
||||||
|
err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(file->m.mdir.mid,
|
||||||
|
REG, +1, NAME(did, name, name_size))));
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (flags & LFS_O_EXCL) {
|
||||||
|
// oh, we really wanted to create a new entry
|
||||||
|
return LFS_ERR_EXIST;
|
||||||
|
}
|
||||||
|
|
||||||
// checkpoint the allocator again since our fixgrm completed some
|
// wrong type?
|
||||||
// work
|
if (tag != LFSR_TAG_REG) {
|
||||||
lfs_alloc_ack(lfs);
|
return LFS_ERR_ISDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're truncating don't bother to read any state, we're
|
||||||
|
// just going to truncate after all
|
||||||
|
if (!(flags & LFS_O_TRUNC)) {
|
||||||
|
// read the inline state
|
||||||
|
err = lfsr_mdir_lookup(lfs, &file->m.mdir,
|
||||||
|
file->m.mdir.mid, LFSR_TAG_INLINED,
|
||||||
|
NULL, &file->inlined);
|
||||||
|
if (err && err != LFS_ERR_NOENT) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO common function for this?
|
||||||
|
// figure out the total size
|
||||||
|
file->size = lfsr_data_size(&file->inlined);
|
||||||
|
|
||||||
|
// allocate buffer if necessary
|
||||||
|
if (file->cfg->buffer) {
|
||||||
|
file->buffer = file->cfg->buffer;
|
||||||
|
} else {
|
||||||
|
file->buffer = lfs_malloc(lfs->cfg->cache_size);
|
||||||
|
if (!file->buffer) {
|
||||||
|
return LFS_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file->buffer_pos = 0;
|
||||||
|
file->buffer_size = 0;
|
||||||
|
|
||||||
|
// add to tracked mdirs
|
||||||
|
lfsr_mdir_addopened(lfs, LFS_TYPE_REG, &file->m);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// default file config
|
||||||
|
static const struct lfs_file_config lfsr_file_defaults = {0};
|
||||||
|
|
||||||
|
int lfsr_file_open(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const char *path, uint32_t flags) {
|
||||||
|
return lfsr_file_opencfg(lfs, file, path, flags, &lfsr_file_defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) {
|
||||||
|
int err = lfsr_file_sync(lfs, file);
|
||||||
|
|
||||||
|
// remove from tracked mdirs
|
||||||
|
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, &file->m);
|
||||||
|
|
||||||
|
// clean up memory
|
||||||
|
if (!file->cfg->buffer) {
|
||||||
|
lfs_free(file->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
void *buffer, lfs_size_t size) {
|
||||||
|
LFS_ASSERT(lfsr_file_isreadable(file->flags));
|
||||||
|
LFS_ASSERT(size <= 0x7fffffff);
|
||||||
|
|
||||||
|
lfs_off_t pos = file->pos;
|
||||||
|
uint8_t *buffer_ = buffer;
|
||||||
|
while (size > 0) {
|
||||||
|
lfs_ssize_t d = size;
|
||||||
|
|
||||||
|
// is the data in our write buffer?
|
||||||
|
if (pos < file->buffer_pos + file->buffer_size) {
|
||||||
|
if (pos >= file->buffer_pos) {
|
||||||
|
d = lfs_min32(
|
||||||
|
size,
|
||||||
|
file->buffer_size - (pos - file->buffer_pos));
|
||||||
|
memcpy(buffer_, &file->buffer[pos - file->buffer_pos], d);
|
||||||
|
|
||||||
|
pos += d;
|
||||||
|
buffer_ += d;
|
||||||
|
size -= d;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffered data takes priority
|
||||||
|
d = lfs_min32(d, file->buffer_pos - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the data inlined?
|
||||||
|
if (pos < file->inlined_pos + lfsr_data_size(&file->inlined)) {
|
||||||
|
if (pos >= file->inlined_pos) {
|
||||||
|
lfsr_data_t inlined = file->inlined;
|
||||||
|
lfsr_data_add(&inlined, pos - file->inlined_pos);
|
||||||
|
d = lfsr_data_read(lfs, &inlined, buffer_, d);
|
||||||
|
if (d < 0) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += d;
|
||||||
|
buffer_ += d;
|
||||||
|
size -= d;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inlined data takes priority
|
||||||
|
d = lfs_min32(d, file->inlined_pos - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// no more data?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_size_t read = pos - file->pos;
|
||||||
|
file->pos = pos;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const void *buffer, lfs_size_t size) {
|
||||||
|
LFS_ASSERT(lfsr_file_iswriteable(file->flags));
|
||||||
|
LFS_ASSERT(size <= 0x7fffffff);
|
||||||
|
|
||||||
|
lfs_off_t pos = file->pos;
|
||||||
|
const uint8_t *buffer_ = buffer;
|
||||||
|
int err;
|
||||||
|
while (size > 0) {
|
||||||
|
// try to fill our write buffer
|
||||||
|
if (pos >= file->buffer_pos
|
||||||
|
&& pos <= file->buffer_pos + file->buffer_size
|
||||||
|
&& pos < file->buffer_pos + lfs->cfg->cache_size) {
|
||||||
|
lfs_size_t d = lfs_min32(
|
||||||
|
size,
|
||||||
|
lfs->cfg->cache_size - (pos - file->buffer_pos));
|
||||||
|
memcpy(&file->buffer[pos - file->buffer_pos], buffer_, d);
|
||||||
|
file->buffer_size = lfs_max32(
|
||||||
|
file->buffer_size,
|
||||||
|
pos+d - file->buffer_pos);
|
||||||
|
|
||||||
|
pos += d;
|
||||||
|
buffer_ += d;
|
||||||
|
size -= d;
|
||||||
|
file->flags |= LFS_F_UNSYNCED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
LFS_ASSERT(false);
|
||||||
|
|
||||||
|
// // flush our write buffer to make it useable, first condition can
|
||||||
|
// // no longer fail
|
||||||
|
// err = lfsr_file_flush(lfs, file);
|
||||||
|
// if (err) {
|
||||||
|
// return err;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs_size_t written = pos - file->pos;
|
||||||
|
file->pos = pos;
|
||||||
|
file->size = lfs_max32(file->size, pos);
|
||||||
|
return written;
|
||||||
|
|
||||||
|
failed:;
|
||||||
|
file->flags |= LFS_F_ERRORED;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
||||||
|
if (file->flags & LFS_F_ERRORED) {
|
||||||
|
// it's not safe to do anything if our file errored
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do nothing if our file has been removed
|
||||||
|
if (file->m.mdir.mid == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out any data we need to
|
||||||
|
int err = lfsr_file_flush(lfs, file);
|
||||||
|
if (err) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file->flags & LFS_F_UNSYNCED) {
|
||||||
|
// TODO
|
||||||
|
// TODO what if buffer_size > inlined_size?
|
||||||
|
LFS_ASSERT(file->buffer_pos == 0);
|
||||||
|
// commit our file's metadata
|
||||||
|
err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS(
|
||||||
|
LFSR_ATTR(file->m.mdir.mid, WIDE(RM), 0, NULL),
|
||||||
|
(file->buffer_size > 0
|
||||||
|
? LFSR_ATTR(file->m.mdir.mid,
|
||||||
|
WIDE(INLINED), 0, BUF(
|
||||||
|
file->buffer, file->buffer_size))
|
||||||
|
: LFSR_ATTR_NOOP)));
|
||||||
|
if (err) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->flags &= ~LFS_F_UNSYNCED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
failed:;
|
||||||
|
file->flags |= LFS_F_ERRORED;
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lfs_soff_t lfsr_file_size(lfs_t *lfs, lfsr_file_t *file) {
|
||||||
|
(void)lfs;
|
||||||
|
return file->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -152,15 +152,9 @@ enum lfs_open_flags {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// internally used flags
|
// internally used flags
|
||||||
#ifndef LFS_READONLY
|
LFS_F_UNFLUSHED = 0x010000, // File's has data that needs to be written
|
||||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
LFS_F_UNSYNCED = 0x020000, // File's metadata does not match storage
|
||||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
LFS_F_ERRORED = 0x040000, // An error occurred during write
|
||||||
#endif
|
|
||||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
|
||||||
#ifndef LFS_READONLY
|
|
||||||
LFS_F_ERRED = 0x080000, // An error occurred during write
|
|
||||||
#endif
|
|
||||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// File seek flags
|
// File seek flags
|
||||||
@@ -280,6 +274,10 @@ struct lfs_config {
|
|||||||
// // can help bound the metadata compaction time. Must be <= block_size.
|
// // can help bound the metadata compaction time. Must be <= block_size.
|
||||||
// // Defaults to block_size when zero.
|
// // Defaults to block_size when zero.
|
||||||
// lfs_size_t metadata_max;
|
// lfs_size_t metadata_max;
|
||||||
|
|
||||||
|
// TODO document
|
||||||
|
lfs_size_t inline_size;
|
||||||
|
lfs_size_t bud_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
// File info structure
|
// File info structure
|
||||||
@@ -427,6 +425,32 @@ typedef struct lfs_mdir {
|
|||||||
lfs_block_t tail[2];
|
lfs_block_t tail[2];
|
||||||
} lfs_mdir_t;
|
} lfs_mdir_t;
|
||||||
|
|
||||||
|
// either an on-disk or in-device data pointer
|
||||||
|
typedef struct lfsr_data {
|
||||||
|
union {
|
||||||
|
// The sign-bit of the size field indicates if the data is in-device
|
||||||
|
// or on-disk.
|
||||||
|
//
|
||||||
|
// After removing the sign bit, the size always encodes the resulting
|
||||||
|
// size on-disk.
|
||||||
|
//
|
||||||
|
lfs_ssize_t size;
|
||||||
|
struct {
|
||||||
|
lfs_ssize_t size;
|
||||||
|
// This leb128 field is a bit of a hack that allows a single leb128
|
||||||
|
// to be injected into lfsr_bd_progdata. Outside of
|
||||||
|
// lfsr_bd_progdata, this field is invalid!
|
||||||
|
int32_t leb128;
|
||||||
|
const uint8_t *buffer;
|
||||||
|
} b;
|
||||||
|
struct {
|
||||||
|
lfs_ssize_t size;
|
||||||
|
lfs_size_t off;
|
||||||
|
lfs_block_t block;
|
||||||
|
} d;
|
||||||
|
} u;
|
||||||
|
} lfsr_data_t;
|
||||||
|
|
||||||
// littlefs directory type
|
// littlefs directory type
|
||||||
typedef struct lfs_dir {
|
typedef struct lfs_dir {
|
||||||
struct lfs_dir *next;
|
struct lfs_dir *next;
|
||||||
@@ -466,6 +490,22 @@ typedef struct lfs_file {
|
|||||||
const struct lfs_file_config *cfg;
|
const struct lfs_file_config *cfg;
|
||||||
} lfs_file_t;
|
} lfs_file_t;
|
||||||
|
|
||||||
|
typedef struct lfsr_file {
|
||||||
|
lfsr_openedmdir_t m;
|
||||||
|
uint32_t flags;
|
||||||
|
lfs_off_t pos;
|
||||||
|
lfs_off_t size;
|
||||||
|
|
||||||
|
lfs_off_t buffer_pos;
|
||||||
|
uint8_t *buffer;
|
||||||
|
lfs_size_t buffer_size;
|
||||||
|
|
||||||
|
lfs_off_t inlined_pos;
|
||||||
|
lfsr_data_t inlined;
|
||||||
|
|
||||||
|
const struct lfs_file_config *cfg;
|
||||||
|
} lfsr_file_t;
|
||||||
|
|
||||||
typedef struct lfs_superblock {
|
typedef struct lfs_superblock {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
lfs_size_t block_size;
|
lfs_size_t block_size;
|
||||||
@@ -638,6 +678,8 @@ int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
|
|||||||
// Returns a negative error code on failure.
|
// Returns a negative error code on failure.
|
||||||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
||||||
const char *path, int flags);
|
const char *path, int flags);
|
||||||
|
int lfsr_file_open(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const char *path, uint32_t flags);
|
||||||
|
|
||||||
// if LFS_NO_MALLOC is defined, lfs_file_open() will fail with LFS_ERR_NOMEM
|
// if LFS_NO_MALLOC is defined, lfs_file_open() will fail with LFS_ERR_NOMEM
|
||||||
// thus use lfs_file_opencfg() with config.buffer set.
|
// thus use lfs_file_opencfg() with config.buffer set.
|
||||||
@@ -656,6 +698,9 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
|||||||
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
||||||
const char *path, int flags,
|
const char *path, int flags,
|
||||||
const struct lfs_file_config *config);
|
const struct lfs_file_config *config);
|
||||||
|
int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const char *path, uint32_t flags,
|
||||||
|
const struct lfs_file_config *config);
|
||||||
|
|
||||||
// Close a file
|
// Close a file
|
||||||
//
|
//
|
||||||
@@ -664,12 +709,14 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
|||||||
//
|
//
|
||||||
// Returns a negative error code on failure.
|
// Returns a negative error code on failure.
|
||||||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
||||||
|
int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file);
|
||||||
|
|
||||||
// Synchronize a file on storage
|
// Synchronize a file on storage
|
||||||
//
|
//
|
||||||
// Any pending writes are written out to storage.
|
// Any pending writes are written out to storage.
|
||||||
// Returns a negative error code on failure.
|
// Returns a negative error code on failure.
|
||||||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
||||||
|
int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file);
|
||||||
|
|
||||||
// Read data from file
|
// Read data from file
|
||||||
//
|
//
|
||||||
@@ -677,6 +724,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
|||||||
// Returns the number of bytes read, or a negative error code on failure.
|
// Returns the number of bytes read, or a negative error code on failure.
|
||||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
||||||
void *buffer, lfs_size_t size);
|
void *buffer, lfs_size_t size);
|
||||||
|
lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
void *buffer, lfs_size_t size);
|
||||||
|
|
||||||
#ifndef LFS_READONLY
|
#ifndef LFS_READONLY
|
||||||
// Write data to file
|
// Write data to file
|
||||||
@@ -687,6 +736,8 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
|||||||
// Returns the number of bytes written, or a negative error code on failure.
|
// Returns the number of bytes written, or a negative error code on failure.
|
||||||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
||||||
const void *buffer, lfs_size_t size);
|
const void *buffer, lfs_size_t size);
|
||||||
|
lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
||||||
|
const void *buffer, lfs_size_t size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Change the position of the file
|
// Change the position of the file
|
||||||
@@ -720,6 +771,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
|
|||||||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
||||||
// Returns the size of the file, or a negative error code on failure.
|
// Returns the size of the file, or a negative error code on failure.
|
||||||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
||||||
|
lfs_soff_t lfsr_file_size(lfs_t *lfs, lfsr_file_t *file);
|
||||||
|
|
||||||
|
|
||||||
/// Directory operations ///
|
/// Directory operations ///
|
||||||
|
|||||||
@@ -1387,6 +1387,8 @@ void perm_run(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -98,7 +98,7 @@ intmax_t bench_define(size_t define);
|
|||||||
|
|
||||||
// a few preconfigured defines that control how benches run
|
// a few preconfigured defines that control how benches run
|
||||||
|
|
||||||
#define BENCH_IMPLICIT_DEFINE_COUNT 12
|
#define BENCH_IMPLICIT_DEFINE_COUNT 14
|
||||||
#define BENCH_GEOMETRY_DEFINE_COUNT 3
|
#define BENCH_GEOMETRY_DEFINE_COUNT 3
|
||||||
|
|
||||||
#define READ_SIZE_i 0
|
#define READ_SIZE_i 0
|
||||||
@@ -107,12 +107,14 @@ intmax_t bench_define(size_t define);
|
|||||||
#define BLOCK_COUNT_i 3
|
#define BLOCK_COUNT_i 3
|
||||||
#define DISK_SIZE_i 4
|
#define DISK_SIZE_i 4
|
||||||
#define CACHE_SIZE_i 5
|
#define CACHE_SIZE_i 5
|
||||||
#define LOOKAHEAD_SIZE_i 6
|
#define INLINE_SIZE_i 6
|
||||||
#define BLOCK_CYCLES_i 7
|
#define BUD_SIZE_i 7
|
||||||
#define ERASE_VALUE_i 8
|
#define LOOKAHEAD_SIZE_i 8
|
||||||
#define ERASE_CYCLES_i 9
|
#define BLOCK_CYCLES_i 9
|
||||||
#define BADBLOCK_BEHAVIOR_i 10
|
#define ERASE_VALUE_i 10
|
||||||
#define POWERLOSS_BEHAVIOR_i 11
|
#define ERASE_CYCLES_i 11
|
||||||
|
#define BADBLOCK_BEHAVIOR_i 12
|
||||||
|
#define POWERLOSS_BEHAVIOR_i 13
|
||||||
|
|
||||||
#define READ_SIZE bench_define(READ_SIZE_i)
|
#define READ_SIZE bench_define(READ_SIZE_i)
|
||||||
#define PROG_SIZE bench_define(PROG_SIZE_i)
|
#define PROG_SIZE bench_define(PROG_SIZE_i)
|
||||||
@@ -120,6 +122,8 @@ intmax_t bench_define(size_t define);
|
|||||||
#define BLOCK_COUNT bench_define(BLOCK_COUNT_i)
|
#define BLOCK_COUNT bench_define(BLOCK_COUNT_i)
|
||||||
#define DISK_SIZE bench_define(DISK_SIZE_i)
|
#define DISK_SIZE bench_define(DISK_SIZE_i)
|
||||||
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
|
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
|
||||||
|
#define INLINE_SIZE bench_define(INLINE_SIZE_i)
|
||||||
|
#define BUD_SIZE bench_define(BUD_SIZE_i)
|
||||||
#define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i)
|
#define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i)
|
||||||
#define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i)
|
#define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i)
|
||||||
#define ERASE_VALUE bench_define(ERASE_VALUE_i)
|
#define ERASE_VALUE bench_define(ERASE_VALUE_i)
|
||||||
@@ -135,6 +139,8 @@ intmax_t bench_define(size_t define);
|
|||||||
BENCH_DEF(BLOCK_COUNT, DISK_SIZE/BLOCK_SIZE ) \
|
BENCH_DEF(BLOCK_COUNT, DISK_SIZE/BLOCK_SIZE ) \
|
||||||
BENCH_DEF(DISK_SIZE, 1024*1024 ) \
|
BENCH_DEF(DISK_SIZE, 1024*1024 ) \
|
||||||
BENCH_DEF(CACHE_SIZE, lfs_max(64, lfs_max(READ_SIZE, PROG_SIZE))) \
|
BENCH_DEF(CACHE_SIZE, lfs_max(64, lfs_max(READ_SIZE, PROG_SIZE))) \
|
||||||
|
BENCH_DEF(INLINE_SIZE, BLOCK_SIZE/4 ) \
|
||||||
|
BENCH_DEF(BUD_SIZE, BLOCK_SIZE/4 ) \
|
||||||
BENCH_DEF(LOOKAHEAD_SIZE, 16 ) \
|
BENCH_DEF(LOOKAHEAD_SIZE, 16 ) \
|
||||||
BENCH_DEF(BLOCK_CYCLES, -1 ) \
|
BENCH_DEF(BLOCK_CYCLES, -1 ) \
|
||||||
BENCH_DEF(ERASE_VALUE, 0xff ) \
|
BENCH_DEF(ERASE_VALUE, 0xff ) \
|
||||||
|
|||||||
@@ -1418,6 +1418,8 @@ static void run_powerloss_none(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1487,6 +1489,8 @@ static void run_powerloss_linear(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1573,6 +1577,8 @@ static void run_powerloss_log(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1657,6 +1663,8 @@ static void run_powerloss_cycles(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1839,6 +1847,8 @@ static void run_powerloss_exhaustive(
|
|||||||
.block_count = BLOCK_COUNT,
|
.block_count = BLOCK_COUNT,
|
||||||
.block_cycles = BLOCK_CYCLES,
|
.block_cycles = BLOCK_CYCLES,
|
||||||
.cache_size = CACHE_SIZE,
|
.cache_size = CACHE_SIZE,
|
||||||
|
.inline_size = INLINE_SIZE,
|
||||||
|
.bud_size = BUD_SIZE,
|
||||||
.lookahead_size = LOOKAHEAD_SIZE,
|
.lookahead_size = LOOKAHEAD_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+13
-7
@@ -97,7 +97,7 @@ intmax_t test_define(size_t define);
|
|||||||
|
|
||||||
// a few preconfigured defines that control how tests run
|
// a few preconfigured defines that control how tests run
|
||||||
|
|
||||||
#define TEST_IMPLICIT_DEFINE_COUNT 12
|
#define TEST_IMPLICIT_DEFINE_COUNT 14
|
||||||
#define TEST_GEOMETRY_DEFINE_COUNT 3
|
#define TEST_GEOMETRY_DEFINE_COUNT 3
|
||||||
|
|
||||||
#define READ_SIZE_i 0
|
#define READ_SIZE_i 0
|
||||||
@@ -106,12 +106,14 @@ intmax_t test_define(size_t define);
|
|||||||
#define BLOCK_COUNT_i 3
|
#define BLOCK_COUNT_i 3
|
||||||
#define DISK_SIZE_i 4
|
#define DISK_SIZE_i 4
|
||||||
#define CACHE_SIZE_i 5
|
#define CACHE_SIZE_i 5
|
||||||
#define LOOKAHEAD_SIZE_i 6
|
#define INLINE_SIZE_i 6
|
||||||
#define BLOCK_CYCLES_i 7
|
#define BUD_SIZE_i 7
|
||||||
#define ERASE_VALUE_i 8
|
#define LOOKAHEAD_SIZE_i 8
|
||||||
#define ERASE_CYCLES_i 9
|
#define BLOCK_CYCLES_i 9
|
||||||
#define BADBLOCK_BEHAVIOR_i 10
|
#define ERASE_VALUE_i 10
|
||||||
#define POWERLOSS_BEHAVIOR_i 11
|
#define ERASE_CYCLES_i 11
|
||||||
|
#define BADBLOCK_BEHAVIOR_i 12
|
||||||
|
#define POWERLOSS_BEHAVIOR_i 13
|
||||||
|
|
||||||
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
||||||
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
||||||
@@ -119,6 +121,8 @@ intmax_t test_define(size_t define);
|
|||||||
#define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i)
|
#define BLOCK_COUNT TEST_DEFINE(BLOCK_COUNT_i)
|
||||||
#define DISK_SIZE TEST_DEFINE(DISK_SIZE_i)
|
#define DISK_SIZE TEST_DEFINE(DISK_SIZE_i)
|
||||||
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
|
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
|
||||||
|
#define INLINE_SIZE TEST_DEFINE(INLINE_SIZE_i)
|
||||||
|
#define BUD_SIZE TEST_DEFINE(BUD_SIZE_i)
|
||||||
#define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i)
|
#define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i)
|
||||||
#define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i)
|
#define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i)
|
||||||
#define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i)
|
#define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i)
|
||||||
@@ -134,6 +138,8 @@ intmax_t test_define(size_t define);
|
|||||||
TEST_DEF(BLOCK_COUNT, DISK_SIZE/BLOCK_SIZE ) \
|
TEST_DEF(BLOCK_COUNT, DISK_SIZE/BLOCK_SIZE ) \
|
||||||
TEST_DEF(DISK_SIZE, 1024*1024 ) \
|
TEST_DEF(DISK_SIZE, 1024*1024 ) \
|
||||||
TEST_DEF(CACHE_SIZE, lfs_max(64, lfs_max(READ_SIZE, PROG_SIZE)) ) \
|
TEST_DEF(CACHE_SIZE, lfs_max(64, lfs_max(READ_SIZE, PROG_SIZE)) ) \
|
||||||
|
TEST_DEF(INLINE_SIZE, BLOCK_SIZE/4 ) \
|
||||||
|
TEST_DEF(BUD_SIZE, BLOCK_SIZE/4 ) \
|
||||||
TEST_DEF(LOOKAHEAD_SIZE, 16 ) \
|
TEST_DEF(LOOKAHEAD_SIZE, 16 ) \
|
||||||
TEST_DEF(BLOCK_CYCLES, -1 ) \
|
TEST_DEF(BLOCK_CYCLES, -1 ) \
|
||||||
TEST_DEF(ERASE_VALUE, 0xff ) \
|
TEST_DEF(ERASE_VALUE, 0xff ) \
|
||||||
|
|||||||
@@ -1,3 +1,435 @@
|
|||||||
|
# Test basic file operations
|
||||||
|
after = ['test_dtree']
|
||||||
|
|
||||||
|
|
||||||
|
# test creation/deletion
|
||||||
|
[cases.test_ftree_create]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
reentrant = true
|
||||||
|
code = '''
|
||||||
|
// format once per test
|
||||||
|
lfs_t lfs;
|
||||||
|
int err = lfsr_mount(&lfs, CFG);
|
||||||
|
if (err) {
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our file with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == 0);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == 0);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => 0;
|
||||||
|
// try reading
|
||||||
|
uint8_t buf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => 0;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# test we can write some data, should be inlined
|
||||||
|
[cases.test_ftree_hello]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
reentrant = true
|
||||||
|
code = '''
|
||||||
|
// format once per test
|
||||||
|
lfs_t lfs;
|
||||||
|
int err = lfsr_mount(&lfs, CFG);
|
||||||
|
if (err) {
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||||
|
uint8_t wbuf[8192];
|
||||||
|
strcpy((char*)wbuf, "Hello World!");
|
||||||
|
lfs_size_t wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our file with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => wsize;
|
||||||
|
// try reading
|
||||||
|
uint8_t rbuf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
|
||||||
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# test we can rewrite a file
|
||||||
|
[cases.test_ftree_trunc]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
reentrant = true
|
||||||
|
code = '''
|
||||||
|
// format once per test
|
||||||
|
lfs_t lfs;
|
||||||
|
int err = lfsr_mount(&lfs, CFG);
|
||||||
|
if (err) {
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||||
|
uint8_t wbuf[8192];
|
||||||
|
strcpy((char*)wbuf, "Oh no!");
|
||||||
|
lfs_size_t wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite the file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
|
||||||
|
strcpy((char*)wbuf, "Hello World!");
|
||||||
|
wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// check our file with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => wsize;
|
||||||
|
// try reading
|
||||||
|
uint8_t rbuf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
|
||||||
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# check for LFS_F_EXCL errors
|
||||||
|
[cases.test_ftree_excl]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
uint8_t wbuf[8192];
|
||||||
|
strcpy((char*)wbuf, "Hello World!");
|
||||||
|
lfs_size_t wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to recreate file, this should error
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => LFS_ERR_EXIST;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our file with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => wsize;
|
||||||
|
// try reading
|
||||||
|
uint8_t buf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# a file is not a directory
|
||||||
|
[cases.test_ftree_file_not_dir]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
uint8_t wbuf[8192];
|
||||||
|
strcpy((char*)wbuf, "Hello World!");
|
||||||
|
lfs_size_t wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to open our file as a directory
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "hello") => LFS_ERR_NOTDIR;
|
||||||
|
|
||||||
|
// try to create a directory on top of our file
|
||||||
|
lfsr_mkdir(&lfs, "hello") => LFS_ERR_EXIST;
|
||||||
|
|
||||||
|
// try to rename a directory onto our file
|
||||||
|
lfsr_mkdir(&lfs, "not_hello") => 0;
|
||||||
|
lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our file with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "not_hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => wsize;
|
||||||
|
// try reading
|
||||||
|
uint8_t buf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, buf, sizeof(buf)) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# a directory is not a file
|
||||||
|
[cases.test_ftree_dir_not_file]
|
||||||
|
defines.REMOUNT = [false, true]
|
||||||
|
code = '''
|
||||||
|
lfs_t lfs;
|
||||||
|
lfsr_format(&lfs, CFG) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
|
||||||
|
// create a directory
|
||||||
|
lfsr_mkdir(&lfs, "hello") => 0;
|
||||||
|
|
||||||
|
// try reading our directory as a file
|
||||||
|
lfsr_file_t file;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
||||||
|
|
||||||
|
// try writing our directory as a file
|
||||||
|
lfsr_file_open(&lfs, &file, "hello", LFS_O_WRONLY) => LFS_ERR_ISDIR;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_ISDIR;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR;
|
||||||
|
lfsr_file_open(&lfs, &file, "hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_ISDIR;
|
||||||
|
|
||||||
|
// try rename a file on top of our directory
|
||||||
|
lfsr_file_open(&lfs, &file, "not_hello",
|
||||||
|
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||||
|
uint8_t wbuf[8192];
|
||||||
|
strcpy((char*)wbuf, "Hello World!");
|
||||||
|
lfs_size_t wsize = strlen((const char*)wbuf);
|
||||||
|
lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize;
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR;
|
||||||
|
|
||||||
|
// remount?
|
||||||
|
if (REMOUNT) {
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
lfsr_mount(&lfs, CFG) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check our dir with stat
|
||||||
|
struct lfs_info info;
|
||||||
|
lfsr_stat(&lfs, "hello", &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
|
||||||
|
// and with dir read
|
||||||
|
lfsr_dir_t dir;
|
||||||
|
lfsr_dir_open(&lfs, &dir, "/") => 0;
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, ".") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "..") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_DIR);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => 0;
|
||||||
|
assert(strcmp(info.name, "not_hello") == 0);
|
||||||
|
assert(info.type == LFS_TYPE_REG);
|
||||||
|
assert(info.size == wsize);
|
||||||
|
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
|
||||||
|
lfsr_dir_close(&lfs, &dir) => 0;
|
||||||
|
|
||||||
|
// did we corrupt our renaming file?
|
||||||
|
// try reading our file
|
||||||
|
lfsr_file_open(&lfs, &file, "not_hello", LFS_O_RDONLY) => 0;
|
||||||
|
// is size correct?
|
||||||
|
lfsr_file_size(&lfs, &file) => wsize;
|
||||||
|
// try reading
|
||||||
|
uint8_t rbuf[8192];
|
||||||
|
lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) => wsize;
|
||||||
|
assert(memcmp(rbuf, wbuf, wsize) == 0);
|
||||||
|
lfsr_file_close(&lfs, &file) => 0;
|
||||||
|
|
||||||
|
lfsr_unmount(&lfs) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#[cases.test_files_simple]
|
#[cases.test_files_simple]
|
||||||
#code = '''
|
#code = '''
|
||||||
Reference in New Issue
Block a user