Allowed modification of unknown file types

This drops the requirement that all file types are introduced with a
related wcompat flag. Instead, the wcompat flag is only required if
modification _would_ leak resources, and we treat unknown file types as
though they are regular files.

This allows modification of unknown file types without the risk of
breaking anything.

To compare with before the unknown-type rework:

Before:

> Unknown file types are allowed and may leak resources if modified,
> so attempted modification (rename/remove) will error with
> LFS_ERR_NOTSUP.

Now:

> Unknown file types are allowed but must not leak resources if
> modified. If an unknown file type would leak resources, it should set
> a related wcompat flag to only allow mounting RDONLY.

Note this includes directories, which can leak bookmarks if removed, so
filesystems using directories should set the LFSR_WCOMPAT_DIR flag.

But we no longer need the LFSR_WCOMPAT_REG/LFSR_WCOMPAT_STICKYNOTE
flags.

---

The real tricky part was getting lfsr_rename to work with unknown types,
as this broke the invariant that we only ever commit tags we know about.

Fixing this required:

- Fetching the non-unknown-mapped tag in lfsr_rename

- Mapping all name tags to LFSR_TAG_NAME in lfsr_rbyd_appendrattr_

- Adopting LFSR_RATTR_NAME for bookmark name tags

  This was broken by the above lfsr_rbyd_appendrattr_ change, but it's
  probably good to handle these the same as other name tags anyways.

This adds a bit of code, but not enough that I think this isn't worth
it (or worth a build-time option):

           code          stack          ctx
  before: 35924           2440          640
  after:  35992 (+0.0%)   2440 (+0.0%)  640 (+0.0%)
This commit is contained in:
Christopher Haster
2025-04-24 14:00:12 -05:00
parent 09c3749d7a
commit a34bcdb5bf
4 changed files with 1246 additions and 47 deletions
+24 -17
View File
@@ -2331,7 +2331,12 @@ static inline lfsr_tag_t lfsr_rattr_dtag(lfsr_rattr_t rattr) {
// lazily tag encoding can be bypassed with explicit data, this is
// necessary to allow copies during compaction, relocation, etc
if (rattr.count >= 0) {
return rattr.tag;
// map all name tags to LFSR_TAG_NAME for simplicity
if (lfsr_tag_suptype(rattr.tag) == LFSR_TAG_NAME) {
return LFSR_TAG_NAME;
} else {
return rattr.tag;
}
} else {
return LFSR_TAG_DATA;
}
@@ -3452,7 +3457,7 @@ static lfsr_data_t lfsr_data_frombtree(const lfsr_btree_t *btree,
static lfsr_data_t lfsr_data_frommptr(const lfs_block_t mptr[static 2],
uint8_t buffer[static LFSR_MPTR_DSIZE]);
// our core rbyd append algorithm
// encode rattrs
static int lfsr_rbyd_appendrattr_(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfsr_rattr_t rattr) {
// tag must not be internal at this point
@@ -3507,7 +3512,6 @@ static int lfsr_rbyd_appendrattr_(lfs_t *lfs, lfsr_rbyd_t *rbyd,
// leb128?
case LFSR_TAG_NAMELIMIT:;
case LFSR_TAG_FILELIMIT:;
case LFSR_TAG_BOOKMARK:;
case LFSR_TAG_DID:;
// leb128s should not exceed 31-bits
LFS_ASSERT(rattr.u.leb128 <= 0x7fffffff);
@@ -3530,9 +3534,6 @@ static int lfsr_rbyd_appendrattr_(lfs_t *lfs, lfsr_rbyd_t *rbyd,
// name?
case LFSR_TAG_NAME:;
case LFSR_TAG_REG:;
case LFSR_TAG_DIR:;
case LFSR_TAG_STICKYNOTE:;
const lfsr_name_t *name = rattr.u.etc;
ctx.u.name.datas[0] = lfsr_data_fromleb128(name->did, ctx.u.name.buf);
ctx.u.name.datas[1] = LFSR_DATA_BUF(name->name, name->name_len);
@@ -3772,7 +3773,7 @@ static void lfsr_rbyd_p_recolor(
}
}
// core rbyd algorithm
// our core rbyd append algorithm
static int lfsr_rbyd_appendrattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
lfsr_srid_t rid, lfsr_rattr_t rattr) {
// must fetch before mutating!
@@ -10300,8 +10301,8 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) {
// commit our bookmark and a grm to self-remove in case of powerloss
lfs_alloc_ckpoint(lfs);
err = lfsr_mdir_commit(lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_LEB128(
LFSR_TAG_BOOKMARK, +1, did_)));
LFSR_RATTR_NAME(
LFSR_TAG_BOOKMARK, +1, did_, NULL, 0)));
if (err) {
return err;
}
@@ -10635,6 +10636,16 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) {
}
}
if (old_tag == LFSR_TAG_UNKNOWN) {
// lookup the actual tag
err = lfsr_rbyd_lookup(lfs, &old_mdir.rbyd,
lfsr_mrid(lfs, old_mdir.mid), LFSR_TAG_MASK8 | LFSR_TAG_NAME,
&old_tag, NULL);
if (err) {
return err;
}
}
// mark old entry for removal with a grm
lfsr_grm_push(lfs, old_mdir.mid);
@@ -13602,17 +13613,13 @@ static int lfs_deinit(lfs_t *lfs) {
#define LFSR_WCOMPAT_NONSTANDARD 0x00000001 // Non-standard filesystem format
#define LFSR_WCOMPAT_RDONLY 0x00000002 // Writing is disallowed
#define LFSR_WCOMPAT_REG 0x00000010 // Regular files in use
#define LFSR_WCOMPAT_DIR 0x00000020 // Directory files in use
#define LFSR_WCOMPAT_STICKYNOTE 0x00000040 // Stickynote files in use
#define LFSR_WCOMPAT_DIR 0x00000010 // Directory files in use
#define LFSR_WCOMPAT_GCKSUM 0x00001000 // Global-checksum in use
// internal
#define LFSR_wcompat_OVERFLOW 0x80000000 // Can't represent all flags
#define LFSR_WCOMPAT_COMPAT \
(LFSR_WCOMPAT_REG \
| LFSR_WCOMPAT_DIR \
| LFSR_WCOMPAT_STICKYNOTE \
(LFSR_WCOMPAT_DIR \
| LFSR_WCOMPAT_GCKSUM)
#define LFSR_OCOMPAT_NONSTANDARD 0x00000001 // Non-standard filesystem format
@@ -14294,9 +14301,9 @@ static int lfsr_formatinited(lfs_t *lfs) {
LFSR_RATTR_LEB128(
LFSR_TAG_FILELIMIT, 0,
lfs->file_limit),
LFSR_RATTR_LEB128(
LFSR_RATTR_NAME(
LFSR_TAG_BOOKMARK, +1,
0)));
0, NULL, 0)));
if (err) {
return err;
}
+1 -5
View File
@@ -185,12 +185,8 @@ FLAGS = [
0x00000001, "Non-standard filesystem format" ),
('WCOMPAT', 'RDONLY',
0x00000002, "Writing is disallowed" ),
('WCOMPAT', 'REG',
0x00000010, "Regular file types in use" ),
('WCOMPAT', 'DIR',
0x00000020, "Directory file types in use" ),
('WCOMPAT', 'STICKYNOTE',
0x00000040, "Stickynote file types in use" ),
0x00000010, "Directory file types in use" ),
('WCOMPAT', 'GCKSUM',
0x00001000, "Global-checksum in use" ),
('wcompat', 'OVERFLOW',
+778 -3
View File
@@ -1146,9 +1146,8 @@ code = '''
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount, note the LFS_M_RDONLY, new types should be added with
// new wcompat flags to prevent resource leaks
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
@@ -1182,9 +1181,785 @@ code = '''
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// open/mkdir should error
lfsr_file_open(&lfs, &file, "b",
LFS_O_RDONLY) => LFS_ERR_NOTSUP;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NOTSUP;
lfsr_mkdir(&lfs, "b") => LFS_ERR_EXIST;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_rm]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi c!", strlen("hi c!")) => strlen("hi c!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// removing unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_remove(&lfs, "b") => 0;
// check that things look reasonable
lfsr_stat(&lfs, "b", &info) => LFS_ERR_NOENT;
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_src]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi c!", strlen("hi c!")) => strlen("hi c!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "b", "c") => 0;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "c", &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_dst]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi c!", strlen("hi c!")) => strlen("hi c!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "c", "b") => 0;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_src_dst]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh hi!", strlen("oh hi!")) => strlen("oh hi!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
path = "c";
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "b", "c") => 0;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "c", &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_noop]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "c",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi c!", strlen("hi c!")) => strlen("hi c!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "b", "b") => 0;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi c!"));
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_notdir]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_mkdir(&lfs, "c") => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "c", "b") => LFS_ERR_NOTDIR;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
[cases.test_mount_incompat_unknown_type_mv_isdir]
in = 'lfs.c'
code = '''
// create a superblock
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
// create some files
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "a",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"hi a!", strlen("hi a!")) => strlen("hi a!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_file_open(&lfs, &file, "b",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
lfsr_file_write(&lfs, &file,
"oh no!", strlen("oh no!")) => strlen("oh no!");
lfsr_file_close(&lfs, &file) => 0;
lfsr_mkdir(&lfs, "c") => 0;
lfsr_unmount(&lfs) => 0;
// change a file's type to something unknown
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path = "b";
lfsr_mdir_t mdir;
lfsr_did_t did;
lfsr_mtree_pathlookup(&lfs, &path,
&mdir, NULL, &did) => 0;
lfsr_mdir_commit(&lfs, &mdir, LFSR_RATTRS(
LFSR_RATTR_CAT(
LFSR_TAG_MASK8 | (LFSR_TAG_NAME + 0x13), 0,
lfsr_data_fromleb128(did, (uint8_t[LFSR_LEB128_DSIZE]){0}),
LFSR_DATA_BUF(path, lfsr_path_namelen(path))))) => 0;
lfsr_unmount(&lfs) => 0;
// mount
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// our file should appear as an unknown type
struct lfs_info info;
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
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);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
// renaming unknown files should still work, if this would leak
// resources the new type should set a wcompat flag
lfsr_rename(&lfs, "b", "c") => LFS_ERR_ISDIR;
// check that things look reasonable after renaming/removing
lfsr_stat(&lfs, "b", &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_open(&lfs, &dir, "/") => 0;
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, ".") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "..") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "a") == 0);
assert(info.type == LFS_TYPE_REG);
assert(info.size == strlen("hi a!"));
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "b") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => 0;
assert(strcmp(info.name, "c") == 0);
assert(info.type == LFS_TYPE_DIR);
assert(info.size == 0);
lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT;
lfsr_dir_close(&lfs, &dir) => 0;
lfsr_unmount(&lfs) => 0;
'''
+443 -22
View File
@@ -4656,29 +4656,70 @@ code = '''
lfsr_dir_open(&lfs, &dir, "coffee/vietnamese") => LFS_ERR_NOTDIR;
lfsr_dir_open(&lfs, &dir, "coffee/thai") => LFS_ERR_NOTDIR;
// note write operations should normally be rejected during mount
// by wcompat flags
// rename paths
lfsr_mkdir(&lfs, "espresso") => 0;
lfsr_rename(&lfs,
"coffee/drip",
"espresso/espresso") => 0;
lfsr_rename(&lfs,
"coffee/coldbrew",
"espresso/americano") => 0;
lfsr_rename(&lfs,
"coffee/turkish",
"espresso/macchiato") => 0;
lfsr_rename(&lfs,
"coffee/tubruk",
"espresso/latte") => 0;
lfsr_rename(&lfs,
"coffee/vietnamese",
"espresso/cappuccino") => 0;
lfsr_rename(&lfs,
"coffee/thai",
"espresso/mocha") => 0;
// stat paths
lfsr_stat(&lfs, "coffee/drip", &info) => 0;
assert(strcmp(info.name, "drip") == 0);
lfsr_stat(&lfs, "espresso/espresso", &info) => 0;
assert(strcmp(info.name, "espresso") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/coldbrew", &info) => 0;
assert(strcmp(info.name, "coldbrew") == 0);
lfsr_stat(&lfs, "espresso/americano", &info) => 0;
assert(strcmp(info.name, "americano") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/turkish", &info) => 0;
assert(strcmp(info.name, "turkish") == 0);
lfsr_stat(&lfs, "espresso/macchiato", &info) => 0;
assert(strcmp(info.name, "macchiato") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/tubruk", &info) => 0;
assert(strcmp(info.name, "tubruk") == 0);
lfsr_stat(&lfs, "espresso/latte", &info) => 0;
assert(strcmp(info.name, "latte") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/vietnamese", &info) => 0;
assert(strcmp(info.name, "vietnamese") == 0);
lfsr_stat(&lfs, "espresso/cappuccino", &info) => 0;
assert(strcmp(info.name, "cappuccino") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/thai", &info) => 0;
assert(strcmp(info.name, "thai") == 0);
lfsr_stat(&lfs, "espresso/mocha", &info) => 0;
assert(strcmp(info.name, "mocha") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/drip", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/coldbrew", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/turkish", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/tubruk", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/vietnamese", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/thai", &info) => LFS_ERR_NOENT;
// remove paths
lfsr_remove(&lfs, "espresso/espresso") => 0;
lfsr_remove(&lfs, "espresso/americano") => 0;
lfsr_remove(&lfs, "espresso/macchiato") => 0;
lfsr_remove(&lfs, "espresso/latte") => 0;
lfsr_remove(&lfs, "espresso/cappuccino") => 0;
lfsr_remove(&lfs, "espresso/mocha") => 0;
// stat paths
lfsr_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT;
lfsr_unmount(&lfs) => 0;
'''
@@ -4857,10 +4898,104 @@ code = '''
lfsr_file_close(&lfs, &file) => 0;
}
// note write operations should normally be rejected during mount
// by wcompat flags
// rename paths
lfsr_mkdir(&lfs, "espresso") => 0;
// bad source
lfsr_rename(&lfs,
"drip/coffee",
"espresso/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coldbrew/coffee",
"espresso/americano") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"turkish/coffee",
"espresso/macchiato") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"tubruk/coffee",
"espresso/latte") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"vietnamese/coffee",
"espresso/cappuccino") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"thai/coffee",
"espresso/mocha") => LFS_ERR_NOTDIR;
// bad destination
lfsr_rename(&lfs,
"coffee/drip",
"drip/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew",
"coldbrew/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish",
"turkish/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk",
"tubruk/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese",
"vietnamese/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai",
"thai/espresso") => LFS_ERR_NOTDIR;
// bad source and bad destination
lfsr_rename(&lfs,
"drip/coffee",
"drip/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coldbrew/coffee",
"coldbrew/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"turkish/coffee",
"turkish/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"tubruk/coffee",
"tubruk/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"vietnamese/coffee",
"vietnamese/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"thai/coffee",
"thai/espresso") => LFS_ERR_NOTDIR;
// here's a weird one, what happens if our rename is also a noop?
lfsr_rename(&lfs,
"drip/coffee",
"drip/coffee") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coldbrew/coffee",
"coldbrew/coffee") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"turkish/coffee",
"turkish/coffee") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"tubruk/coffee",
"tubruk/coffee") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"vietnamese/coffee",
"vietnamese/coffee") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"thai/coffee",
"thai/coffee") => LFS_ERR_NOTDIR;
// remove paths
lfsr_stat(&lfs, "drip/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "coldbrew/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "turkish/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "tubruk/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "vietnamese/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "thai/espresso", &info) => LFS_ERR_NOTDIR;
// stat paths
lfsr_stat(&lfs, "drip/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "coldbrew/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "turkish/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "tubruk/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "vietnamese/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "thai/espresso", &info) => LFS_ERR_NOTDIR;
lfsr_stat(&lfs, "coffee/drip", &info) => 0;
assert(strcmp(info.name, "drip") == 0);
assert(info.type == ((DIR) ? LFS_TYPE_DIR : LFS_TYPE_REG));
@@ -5005,10 +5140,104 @@ code = '''
lfsr_dir_open(&lfs, &dir, "coffee/vietnamese/////") => LFS_ERR_NOTDIR;
lfsr_dir_open(&lfs, &dir, "coffee/thai//////") => LFS_ERR_NOTDIR;
// note write operations should normally be rejected during mount
// by wcompat flags
// rename paths
lfsr_mkdir(&lfs, "espresso") => 0;
// bad source
lfsr_rename(&lfs,
"coffee/drip//////",
"espresso/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/////",
"espresso/americano") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish////",
"espresso/macchiato") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk///",
"espresso/latte") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese//",
"espresso/cappuccino") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/",
"espresso/mocha") => LFS_ERR_NOTDIR;
// bad destination
lfsr_rename(&lfs,
"coffee/drip",
"espresso/espresso/") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew",
"espresso/americano//") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish",
"espresso/macchiato///") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk",
"espresso/latte////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese",
"espresso/cappuccino/////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai",
"espresso/mocha//////") => LFS_ERR_NOTDIR;
// bad source and bad destination
lfsr_rename(&lfs,
"coffee/drip//////",
"espresso/espresso/") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/////",
"espresso/americano//") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish////",
"espresso/macchiato///") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk///",
"espresso/latte////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese//",
"espresso/cappuccino/////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/",
"espresso/mocha//////") => LFS_ERR_NOTDIR;
// here's a weird one, what happens if our rename is also a noop?
lfsr_rename(&lfs,
"coffee/drip//////",
"coffee/drip//////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/////",
"coffee/coldbrew/////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish////",
"coffee/turkish////") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk///",
"coffee/tubruk///") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese//",
"coffee/vietnamese//") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/",
"coffee/thai/") => LFS_ERR_NOTDIR;
// remove paths
lfsr_remove(&lfs, "coffee/drip/") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/coldbrew//") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/turkish///") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/tubruk////") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/vietnamese/////") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/thai//////") => LFS_ERR_NOTDIR;
// stat paths
lfsr_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/drip", &info) => 0;
assert(strcmp(info.name, "drip") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
@@ -5153,10 +5382,104 @@ code = '''
lfsr_dir_open(&lfs, &dir, "coffee/vietnamese/././././.") => LFS_ERR_NOTDIR;
lfsr_dir_open(&lfs, &dir, "coffee/thai/./././././.") => LFS_ERR_NOTDIR;
// note write operations should normally be rejected during mount
// by wcompat flags
// rename paths
lfsr_mkdir(&lfs, "espresso") => 0;
// bad source
lfsr_rename(&lfs,
"coffee/drip/./././././.",
"espresso/espresso") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/././././.",
"espresso/americano") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish/./././.",
"espresso/macchiato") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk/././.",
"espresso/latte") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese/./.",
"espresso/cappuccino") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/.",
"espresso/mocha") => LFS_ERR_NOTDIR;
// bad destination
lfsr_rename(&lfs,
"coffee/drip",
"espresso/espresso/.") => LFS_ERR_NOENT;
lfsr_rename(&lfs,
"coffee/coldbrew",
"espresso/americano/./.") => LFS_ERR_NOENT;
lfsr_rename(&lfs,
"coffee/turkish",
"espresso/macchiato/././.") => LFS_ERR_NOENT;
lfsr_rename(&lfs,
"coffee/tubruk",
"espresso/latte/./././.") => LFS_ERR_NOENT;
lfsr_rename(&lfs,
"coffee/vietnamese",
"espresso/cappuccino/././././.") => LFS_ERR_NOENT;
lfsr_rename(&lfs,
"coffee/thai",
"espresso/mocha/./././././.") => LFS_ERR_NOENT;
// bad source and bad destination
lfsr_rename(&lfs,
"coffee/drip/./././././.",
"espresso/espresso/.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/././././.",
"espresso/americano/./.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish/./././.",
"espresso/macchiato/././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk/././.",
"espresso/latte/./././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese/./.",
"espresso/cappuccino/././././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/.",
"espresso/mocha/./././././.") => LFS_ERR_NOTDIR;
// here's a weird one, what happens if our rename is also a noop?
lfsr_rename(&lfs,
"coffee/drip/./././././.",
"coffee/drip/./././././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/coldbrew/././././.",
"coffee/coldbrew/././././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/turkish/./././.",
"coffee/turkish/./././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/tubruk/././.",
"coffee/tubruk/././.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/vietnamese/./.",
"coffee/vietnamese/./.") => LFS_ERR_NOTDIR;
lfsr_rename(&lfs,
"coffee/thai/.",
"coffee/thai/.") => LFS_ERR_NOTDIR;
// remove paths
lfsr_remove(&lfs, "coffee/drip/.") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/coldbrew/./.") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/turkish/././.") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/tubruk/./././.") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/vietnamese/././././.") => LFS_ERR_NOTDIR;
lfsr_remove(&lfs, "coffee/thai/./././././.") => LFS_ERR_NOTDIR;
// stat paths
lfsr_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/drip", &info) => 0;
assert(strcmp(info.name, "drip") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
@@ -5281,11 +5604,109 @@ code = '''
lfsr_dir_open(&lfs, &dir, "coffee/vietnamese/../../../../..") => LFS_ERR_INVAL;
lfsr_dir_open(&lfs, &dir, "coffee/thai/../../../../../..") => LFS_ERR_INVAL;
// note write operations should normally be rejected during mount
// by wcompat flags
// rename paths
lfsr_mkdir(&lfs, "espresso") => 0;
// bad source
lfsr_rename(&lfs,
"coffee/drip/../../../../../..",
"espresso/espresso") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/coldbrew/../../../../..",
"espresso/americano") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/turkish/../../../..",
"espresso/macchiato") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/tubruk/../../..",
"espresso/latte") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/vietnamese/../..",
"espresso/cappuccino") => LFS_ERR_INVAL;
// this one works
lfsr_rename(&lfs,
"coffee/thai/..",
"espresso/mocha") => 0;
lfsr_rename(&lfs,
"espresso/mocha",
"coffee") => 0;
// bad destination
lfsr_rename(&lfs,
"coffee/drip",
"espresso/espresso/..") => LFS_ERR_ISDIR;
lfsr_rename(&lfs,
"coffee/coldbrew",
"espresso/americano/../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/turkish",
"espresso/macchiato/../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/tubruk",
"espresso/latte/../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/vietnamese",
"espresso/cappuccino/../../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/thai",
"espresso/mocha/../../../../../..") => LFS_ERR_INVAL;
// bad source and bad destination
lfsr_rename(&lfs,
"coffee/drip/../../../../../..",
"espresso/espresso/..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/coldbrew/../../../../..",
"espresso/americano/../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/turkish/../../../..",
"espresso/macchiato/../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/tubruk/../../..",
"espresso/latte/../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/vietnamese/../..",
"espresso/cappuccino/../../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/thai/..",
"espresso/mocha/../../../../../..") => LFS_ERR_INVAL;
// here's a weird one, what happens if our rename is also a noop?
lfsr_rename(&lfs,
"coffee/drip/../../../../../..",
"coffee/drip/../../../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/coldbrew/../../../../..",
"coffee/coldbrew/../../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/turkish/../../../..",
"coffee/turkish/../../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/tubruk/../../..",
"coffee/tubruk/../../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/vietnamese/../..",
"coffee/vietnamese/../..") => LFS_ERR_INVAL;
lfsr_rename(&lfs,
"coffee/thai/..",
"coffee/thai/..") => 0;
// remove paths
lfsr_remove(&lfs, "coffee/drip/..") => LFS_ERR_NOTEMPTY;
lfsr_remove(&lfs, "coffee/coldbrew/../..") => LFS_ERR_INVAL;
lfsr_remove(&lfs, "coffee/turkish/../../..") => LFS_ERR_INVAL;
lfsr_remove(&lfs, "coffee/tubruk/../../../..") => LFS_ERR_INVAL;
lfsr_remove(&lfs, "coffee/vietnamese/../../../../..") => LFS_ERR_INVAL;
lfsr_remove(&lfs, "coffee/thai/../../../../../..") => LFS_ERR_INVAL;
// stat paths
lfsr_stat(&lfs, "espresso/espresso", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/americano", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/macchiato", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/latte", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/cappuccino", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "espresso/mocha", &info) => LFS_ERR_NOENT;
lfsr_stat(&lfs, "coffee/drip", &info) => 0;
assert(strcmp(info.name, "drip") == 0);
assert(info.type == LFS_TYPE_UNKNOWN);
lfsr_stat(&lfs, "coffee/coldbrew", &info) => 0;