diff --git a/lfs.c b/lfs.c index f57b8916..84f85284 100644 --- a/lfs.c +++ b/lfs.c @@ -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; } diff --git a/scripts/dbgflags.py b/scripts/dbgflags.py index 21ecb34b..0cb58e87 100755 --- a/scripts/dbgflags.py +++ b/scripts/dbgflags.py @@ -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', diff --git a/tests/test_mount.toml b/tests/test_mount.toml index b8f58be3..cfbca8f4 100644 --- a/tests/test_mount.toml +++ b/tests/test_mount.toml @@ -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; +''' diff --git a/tests/test_paths.toml b/tests/test_paths.toml index 827c83b4..bb321778 100644 --- a/tests/test_paths.toml +++ b/tests/test_paths.toml @@ -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;