Added better error handling of unknown filetypes

This is the tradeoff of not erroring on unknown filetypes during mount.

- lfsr_file_open and lfsr_mtree_pathlookup now returns LFS_ERR_NOTSUP
  instead of LFS_ERR_NOTDIR/LFS_ERR_ISDIR if it encounters an unkown
  filetype.

  This gets a bit subtle. You might think LFS_ERR_NOTDIR is reasonable,
  but it's possible for our unknown filetype to be something dir-like.

  Symlinks are an excellent example.

- lfsr_remove/lfsr_rename now bail with LFS_ERR_NOTSUP if encountering
  an unknown filetype.

  This conflicts with the POSIX philosophy of remove always being
  allowed, but I'm not sure what other option there is. Maybe allowing
  removes when mounted with LFS_M_FORCE?

  We can't just allow removes by default because of the risk of leaking
  resources. Directories being the main example of this (need to clean
  up bookmarks).

  Maybe leaky filetypes should also set WCOMPAT flags?

Not doing something is cheaper than doing something, so unfortunately
this costs us more than what we saved from dropping the orphan/unknown
scan during mount:

                              code          stack          ctx
  bail:                      38120           2624          725
  no-error-no-bail (before): 38020 (-0.3%)   2624 (+0.0%)  752 (+0.0%)
  error-no-bail (after):     38140 (+0.1%)   2624 (+0.0%)  752 (+0.0%)

But this is probably worth it for the extra flexibility.
This commit is contained in:
Christopher Haster
2024-12-30 22:38:35 -06:00
parent 6e63920338
commit 1a99c195f0
2 changed files with 113 additions and 3 deletions
+93
View File
@@ -902,3 +902,96 @@ code = '''
lfsr_mount(&lfs, LFS_M_RDONLY, CFG) => LFS_ERR_NOTSUP;
'''
# test what happens if we find an unknown file type
[cases.test_mount_incompat_unknown_type]
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_RATS(
LFSR_RAT_NAME(
LFSR_TAG_SUB | (LFSR_TAG_NAME + 0x13), 0,
did, 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 == 0x13);
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 == 0x13);
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/renaming unknown files should return NOTSUP, we could
// remove the metadata entry, but we would probably leak stuff
lfsr_remove(&lfs, "b") => LFS_ERR_NOTSUP;
lfsr_rename(&lfs, "b", "d") => LFS_ERR_NOTSUP;
lfsr_rename(&lfs, "b", "c") => LFS_ERR_NOTSUP;
lfsr_rename(&lfs, "a", "b") => LFS_ERR_NOTSUP;
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;
'''