diff --git a/lfs.c b/lfs.c index 56cdc2e5..3b28f771 100644 --- a/lfs.c +++ b/lfs.c @@ -7329,6 +7329,22 @@ static bool lfsr_omdir_ismidopen(lfs_t *lfs, lfsr_smid_t mid) { return false; } +// like lfsr_omdir_ismidopen but ignores zombies/desynced files +static bool lfsr_omdir_ismidalive(lfs_t *lfs, lfsr_smid_t mid) { + for (lfsr_omdir_t *o = lfs->omdirs; o; o = o->next) { + // we really only care about regular open files here, all + // others are either transient (dirs) or fake (orphans) + if (lfsr_o_type(o->flags) == LFS_TYPE_REG + && o->mdir.mid == mid + && !lfsr_o_iszombie(o->flags) + && !lfsr_o_isdesync(o->flags)) { + return true; + } + } + + return false; +} + // traversal invalidation things // needed in lfsr_omdir_clobber @@ -10744,7 +10760,6 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { if (err && !(err == LFS_ERR_NOENT && lfsr_path_islast(new_path))) { return err; } - // already exists? bool exists = (err != LFS_ERR_NOENT); // there are a few cases we need to watch out for @@ -11569,9 +11584,10 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, if (err && !(err == LFS_ERR_NOENT && lfsr_path_islast(path))) { return err; } + bool exists = err != LFS_ERR_NOENT; // creating a new entry? - if (err == LFS_ERR_NOENT || tag == LFSR_TAG_STICKYNOTE) { + if (!exists || tag == LFSR_TAG_STICKYNOTE) { if (!lfsr_o_iscreat(flags)) { return LFS_ERR_NOENT; } @@ -11582,15 +11598,27 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, return LFS_ERR_NOTDIR; } - // check that name fits - lfs_size_t name_len = lfsr_path_namelen(path); - if (name_len > lfs->name_limit) { - return LFS_ERR_NAMETOOLONG; + // if we're EXCL and we found a stickynote, check if the file + // is open and not zombied/desynced + // + // we error here even though the file isn't created yet so + // EXCL only lets one create through (ignoring desync+sync + // shenanigans) + if (exists + && lfsr_o_isexcl(flags) + && lfsr_omdir_ismidalive(lfs, file->o.o.mdir.mid)) { + return LFS_ERR_EXIST; } // create a stickynote entry if we don't have one, this reserves the // mid until first sync - if (err == LFS_ERR_NOENT) { + if (!exists) { + // check that name fits + lfs_size_t name_len = lfsr_path_namelen(path); + if (name_len > lfs->name_limit) { + return LFS_ERR_NAMETOOLONG; + } + lfs_alloc_ckpoint(lfs); err = lfsr_mdir_commit(lfs, &file->o.o.mdir, LFSR_RATS( LFSR_RAT_NAME( diff --git a/tests/test_forphans.toml b/tests/test_forphans.toml index 2cb35102..a810b929 100644 --- a/tests/test_forphans.toml +++ b/tests/test_forphans.toml @@ -468,7 +468,7 @@ code = ''' // but we should still recieve sync broadcasts on sync/close lfsr_file_t file__; lfsr_file_open(&lfs, &file__, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_RDWR | LFS_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { @@ -609,7 +609,7 @@ code = ''' // open a second reference lfsr_file_t file__; lfsr_file_open(&lfs, &file__, "batman", - LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_WRONLY | LFS_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { @@ -814,7 +814,7 @@ code = ''' // and a third for checking sync broadcasts lfsr_file_t file___; lfsr_file_open(&lfs, &file___, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_RDWR | LFS_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { @@ -1275,7 +1275,7 @@ code = ''' // and a third for checking sync broadcasts lfsr_file_t file___; lfsr_file_open(&lfs, &file___, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_RDWR | LFS_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) {