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...
This commit is contained in:
Christopher Haster
2023-12-01 00:31:19 -06:00
parent abbd2d6c3f
commit 939dd2145a
3 changed files with 540 additions and 34 deletions
+33 -23
View File
@@ -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;
}
+2 -4
View File
@@ -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) {
+505 -7
View File
@@ -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]