From 939dd2145a52ff9d3b62adf92b8c8575c4c09aa7 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 1 Dec 2023 00:31:19 -0600 Subject: [PATCH] Added some corner-case tests, fixed related bugs/POSIX nuances POSIX is notoriously full of subtle and confusing nuances. Not through any fault of POSIX, but as a result of trying to describe a complex system with simple and easy to use operations. Corner cases fixed here: - rename("dir", "file") => ENOTDIR This is the main surprise to me, and a mistake on my part. I thought EISDIR would be appropriate for any renames with mismatched types, since both involve a directory. It would be simpler code-wise, and avoid ambiguity around if "file" is not a dir, or some other file exists in the file's path. But I guess ENOTDIR makes more sense if you think of the destination as the target being operated on. - remove("/") => EINVAL - rename("/", "x") => EINVAL - rename("x", "/") => ENOTEMPTY - open("/") => EISDIR It's a bit difficult to lookup what error codes around root operations should be, since they mostly end up as EPERM on modern systems, but this doesn't really make sense for littlefs. The solution chosen here is to prefer directory-related errors (EISDIR, ENOTEMPTY) when possible, and fall back to EINVAL when the only issue is that the target is the root directory. Also I tweaked lfsr_mtree_pathlookup a bit so mid=0 indicates the target is the root and mid=-1 indicates the target can't be created (because of a missing directory). I think using mid=0 for the latter is a leftover from when mid=-1 was a bit of a mess... --- lfs.c | 56 +++-- tests/test_dirs.toml | 6 +- tests/test_files.toml | 512 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 540 insertions(+), 34 deletions(-) diff --git a/lfs.c b/lfs.c index 69b3bc32..9b01076c 100644 --- a/lfs.c +++ b/lfs.c @@ -6950,6 +6950,8 @@ enum { LFSR_DID_ROOT = 0, }; +// TODO this function may need another look over +// // lookup full paths in our mtree // // if not found, mdir_/did_/name_ will at least be set up @@ -6960,16 +6962,13 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const char *path, lfsr_mdir_t *mdir_, lfsr_tag_t *tag_, lfsr_did_t *did_, const char **name_, lfs_size_t *name_size_) { // setup root - lfsr_mdir_t mdir; - mdir.mid = 0; + lfsr_mdir_t mdir = {.mid = 0}; lfsr_tag_t tag = LFSR_TAG_DIR; lfsr_did_t did = LFSR_DID_ROOT; + // use mid=-1 to indicate we can't even create the path if (mdir_) { - *mdir_ = mdir; - } - if (tag_) { - *tag_ = tag; + mdir_->mid = -1; } // we reduce path to a single name if we can find it @@ -7013,10 +7012,11 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const char *path, // found end of path, we must be done parsing our path now if (name[0] == '\0') { - // generally we don't allow operations that change our root, - // report root as inval, but let upper layers intercept this - if (lfsr_mid_isroot(mdir.mid)) { - return LFS_ERR_INVAL; + if (mdir_) { + *mdir_ = mdir; + } + if (tag_) { + *tag_ = tag; } return 0; } @@ -7048,8 +7048,7 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const char *path, return err; } - // keep track of what we've seen, but only if we're the last name - // in our path + // keep track of where to insert if we are the last name in our path if (strchr(name, '/') == NULL) { if (mdir_) { *mdir_ = mdir; @@ -7068,8 +7067,7 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const char *path, } } - // error if not found, note we update things first so mdir - // gets updated with where to insert correctly + // error if not found if (err == LFS_ERR_NOENT) { return LFS_ERR_NOENT; } @@ -8543,11 +8541,11 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { err = lfsr_mtree_pathlookup(lfs, path, &mdir, NULL, &did, &name, &name_size); - if (err && (err != LFS_ERR_NOENT || lfsr_mdir_isroot(&mdir))) { + if (err && (err != LFS_ERR_NOENT || mdir.mid == -1)) { return err; } - // woah, already exists? + // already exists? if (err != LFS_ERR_NOENT) { return LFS_ERR_EXIST; } @@ -8672,6 +8670,11 @@ int lfsr_remove(lfs_t *lfs, const char *path) { return err; } + // as funny as it would be, you can't remove the root + if (lfsr_mdir_isroot(&mdir)) { + return LFS_ERR_INVAL; + } + // if we're removing a directory, we need to also remove the // bookmark entry lfsr_grm_t grm = lfs->grm; @@ -8759,6 +8762,11 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { return err; } + // as funny as it would be, you can't rename the root + if (lfsr_mdir_isroot(&old_mdir)) { + return LFS_ERR_INVAL; + } + // mark old entry for removal with a grm lfsr_grm_t grm = lfs->grm; lfsr_grm_pushrm(&grm, old_mdir.mid); @@ -8772,7 +8780,7 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { err = lfsr_mtree_pathlookup(lfs, new_path, &new_mdir, &new_tag, &new_did, &new_name, &new_name_size); - if (err && (err != LFS_ERR_NOENT || lfsr_mdir_isroot(&new_mdir))) { + if (err && (err != LFS_ERR_NOENT || new_mdir.mid == -1)) { return err; } bool exists = (err != LFS_ERR_NOENT); @@ -8793,7 +8801,9 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { } else { // renaming different types is an error if (old_tag != new_tag) { - return LFS_ERR_ISDIR; + return (new_tag == LFSR_TAG_DIR) + ? LFS_ERR_ISDIR + : LFS_ERR_NOTDIR; } // TODO is it? is this check necessary? @@ -8953,12 +8963,12 @@ int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { int err = lfsr_mtree_pathlookup(lfs, path, &mdir, &tag, NULL, &name, &name_size); - if (err && err != LFS_ERR_INVAL) { + if (err) { return err; } // special case for root - if (err == LFS_ERR_INVAL) { + if (lfsr_mdir_isroot(&mdir)) { strcpy(info->name, "/"); info->type = LFS_TYPE_DIR; return 0; @@ -8975,7 +8985,7 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) { int err = lfsr_mtree_pathlookup(lfs, path, &mdir, &tag, NULL, NULL, NULL); - if (err && err != LFS_ERR_INVAL) { + if (err) { return err; } @@ -8985,7 +8995,7 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) { } // read our did from the mdir, unless we're root - if (err == LFS_ERR_INVAL) { + if (lfsr_mdir_isroot(&mdir)) { dir->did = 0; } else { lfsr_data_t data; @@ -9266,7 +9276,7 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, int err = lfsr_mtree_pathlookup(lfs, path, &file->mdir, &tag, &did, &name, &name_size); - if (err && err != LFS_ERR_NOENT) { + if (err && (err != LFS_ERR_NOENT || file->mdir.mid == -1)) { return err; } diff --git a/tests/test_dirs.toml b/tests/test_dirs.toml index f1e8153b..788b0b8b 100644 --- a/tests/test_dirs.toml +++ b/tests/test_dirs.toml @@ -281,16 +281,14 @@ code = ''' } // try to make root, which doesn't make sense - err = lfsr_mkdir(&lfs, "/"); - assert(err == LFS_ERR_EXIST || err == LFS_ERR_INVAL); + lfsr_mkdir(&lfs, "/") => LFS_ERR_EXIST; // make a directory err = lfsr_mkdir(&lfs, "ardvark"); assert(!err || (TEST_PLS && err == LFS_ERR_EXIST)); // try to make root, which doesn't make sense - err = lfsr_mkdir(&lfs, "/"); - assert(err == LFS_ERR_EXIST || err == LFS_ERR_INVAL); + lfsr_mkdir(&lfs, "/") => LFS_ERR_EXIST; // remount? if (REMOUNT) { diff --git a/tests/test_files.toml b/tests/test_files.toml index ce7eed7b..741ae458 100644 --- a/tests/test_files.toml +++ b/tests/test_files.toml @@ -288,7 +288,7 @@ code = ''' // try to rename a directory onto our file lfsr_mkdir(&lfs, "not_hello") => 0; - lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_ISDIR; + lfsr_rename(&lfs, "not_hello", "hello") => LFS_ERR_NOTDIR; // remount? if (REMOUNT) { @@ -426,16 +426,16 @@ code = ''' // try reading our root as a file lfsr_file_t file; - lfsr_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_INVAL; + lfsr_file_open(&lfs, &file, "/", LFS_O_RDONLY) => LFS_ERR_ISDIR; // try writing our root as a file - lfsr_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_INVAL; + lfsr_file_open(&lfs, &file, "/", LFS_O_WRONLY) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "/", - LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_INVAL; + LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "/", - LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_INVAL; + LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_ISDIR; lfsr_file_open(&lfs, &file, "/", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_INVAL; + 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", @@ -446,7 +446,82 @@ code = ''' lfsr_file_write(&lfs, &file, wbuf, wsize) => wsize; lfsr_file_close(&lfs, &file) => 0; - lfsr_rename(&lfs, "not_hello", "/") => LFS_ERR_INVAL; + lfsr_rename(&lfs, "not_hello", "/") => LFS_ERR_ISDIR; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check our root with stat + struct lfs_info info; + lfsr_stat(&lfs, "/", &info) => 0; + assert(strcmp(info.name, "/") == 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, "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]; + memset(rbuf, 0xaa, sizeof(rbuf)); + 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; +''' + +# an invalid path is also not a file (kind of?) +[cases.test_files_noent_not_file] +defines.REMOUNT = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // try reading our invalid path as a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "no/hello", LFS_O_RDONLY) => LFS_ERR_NOENT; + + // try writing our root as a file + lfsr_file_open(&lfs, &file, "no/hello", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file, "no/hello", + LFS_O_WRONLY | LFS_O_TRUNC) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file, "no/hello", + LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file, "no/hello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => LFS_ERR_NOENT; + + // try rename a file on top of our invalid path + 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", "no/hello") => LFS_ERR_NOENT; // remount? if (REMOUNT) { @@ -838,6 +913,429 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# test renaming a file to itself +[cases.test_files_mv_noop] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.REMOUNT = [false, true] +defines.CACHE_SIZE = 64 +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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // rename the file + lfsr_rename(&lfs, "amethyst", "amethyst") => 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, "amethyst", &info) => 0; + assert(strcmp(info.name, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // 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, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "amethyst", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test renaming a file onto a dir +[cases.test_files_mv_not_file] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.REMOUNT = [false, true] +defines.CACHE_SIZE = 64 +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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create a dir + lfsr_mkdir(&lfs, "basalt") => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // rename the file + lfsr_rename(&lfs, "amethyst", "basalt") => LFS_ERR_ISDIR; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check that nothing changed in our file/dir with stat + struct lfs_info info; + lfsr_stat(&lfs, "amethyst", &info) => 0; + assert(strcmp(info.name, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_stat(&lfs, "basalt", &info) => 0; + assert(strcmp(info.name, "basalt") == 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, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "basalt") == 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, "amethyst", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test renaming a dir onto a file +[cases.test_files_mv_not_dir] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.REMOUNT = [false, true] +defines.CACHE_SIZE = 64 +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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // create a dir + lfsr_mkdir(&lfs, "basalt") => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // rename the dir + lfsr_rename(&lfs, "basalt", "amethyst") => LFS_ERR_NOTDIR; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check that nothing changed in our file/dir with stat + struct lfs_info info; + lfsr_stat(&lfs, "amethyst", &info) => 0; + assert(strcmp(info.name, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_stat(&lfs, "basalt", &info) => 0; + assert(strcmp(info.name, "basalt") == 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, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "basalt") == 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, "amethyst", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test renaming a file onto root +[cases.test_files_mv_not_root] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.REMOUNT = [false, true] +defines.CACHE_SIZE = 64 +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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // rename the file + lfsr_rename(&lfs, "amethyst", "/") => LFS_ERR_ISDIR; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check that nothing changed in our file/dir with stat + struct lfs_info info; + lfsr_stat(&lfs, "amethyst", &info) => 0; + assert(strcmp(info.name, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // 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, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "amethyst", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test renaming a file onto an invalid path +[cases.test_files_mv_not_noent] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.REMOUNT = [false, true] +defines.CACHE_SIZE = 64 +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, "amethyst", LFS_O_WRONLY | LFS_O_CREAT) => 0; + uint8_t wbuf[SIZE]; + uint32_t prng = 42; + for (lfs_size_t i = 0; i < SIZE; i++) { + wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // rename the file + lfsr_rename(&lfs, "amethyst", "no/amethyst") => LFS_ERR_NOENT; + + // remount? + if (REMOUNT) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, CFG) => 0; + } + + // check that nothing changed in our file/dir with stat + struct lfs_info info; + lfsr_stat(&lfs, "amethyst", &info) => 0; + assert(strcmp(info.name, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + + // 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, "amethyst") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + + // try reading our file + lfsr_file_open(&lfs, &file, "amethyst", LFS_O_RDONLY) => 0; + // is size correct? + lfsr_file_size(&lfs, &file) => SIZE; + // try reading + uint8_t rbuf[2*SIZE]; + memset(rbuf, 0xaa, 2*SIZE); + lfsr_file_read(&lfs, &file, rbuf, 2*SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + # TODO # [cases.test_files_rm]