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:
Christopher Haster
2023-09-17 11:02:30 -05:00
parent e7bf5ad82f
commit c74ec1c133
7 changed files with 941 additions and 147 deletions
+410 -124
View File
@@ -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;
}
+61 -9
View File
@@ -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 ///
+2
View File
@@ -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
View File
@@ -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 ) \
+10
View File
@@ -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
View File
@@ -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 = '''