From 76493142e72229fbdea2a34e77bb4a4e91ff7220 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 22 Apr 2025 15:24:42 -0500 Subject: [PATCH] Reworked stickynote API, exposed LFS_TYPE_STICKYNOTE to users This adds the LFS_TYPE_STICKYNOTE type, allowing users to interact with stickynotes as long as they aren't orphaned. This hopefully solves the long-standing mess that was the LFS_O_EXCL API. --- As for what I mean by orphaned vs non-orphaned stickynotes: Non-orphaned stickynotes represent files that have been "created" (via LFS_O_CREAT), but not "committed" (via sync/close). You can still close and convert the stickynote to a reg file, so these aren't orphans. These are also called "uncreated" files in some parts of the codebase: - open+O_CREAT -> non-orphaned stickynote (uncreated file) Orphaned stickynotes are possible by either removing an open file, or desyncing a file before sync/close. These are still invisible to the user and will be eventually cleaned up after the last file handle is closed: - open+remove -> orphaned stickynote (zombied file) - open+O_CREAT+desync+close -> orphaned stickynote (orphaned file) Desynced files are a bit special. Even though they technically aren't orphaned, they also behave like orphaned file handles: - open+O_CREAT+close -> orphaned stickynote (desynced file) The idea is this mimics the state of files post-close, and allows for some tricks like using a desync file as a temporary file with no observable effects on the filesystem. --- The motivation for this comes from staring at the LFS_O_EXCL API for too long and realizing the problem is that littlefs's API contradicts itself when it comes to whether or not uncreated files exist. This solution is to consistently treat uncreated files as though they exist (the alternative would make LFS_O_EXCL pretty much useless), but I really didn't want to do this as having what appears to be normal files disappear after powerloss risks confusion. The compromise here is to give these files a special type, repurposing the internal LFS_TAG_STICKYNOTE, which hopefully hints to the user these won't behave like normal files. If the user is more interested in POSIX compatibility, they can always map these to either LFS_TYPE_REG or LFS_ERR_NOENT, whichever they think is the least confusing. As a quirk of littlefs's API, stickynotes should never actually contain any data, and will always have size 0. However they can have custom attributes assigned now (which is I guess ok? also TODO should probably test this). --- The implementation right now is a bit naive, I mostly just wanted to get the tests working again in this new model. It may be possible to claw back some of this code cost: code stack ctx before: 35740 2440 640 after: 35952 (+0.6%) 2440 (+0.0%) 640 (+0.0%) --- lfs.c | 129 +- lfs.h | 2 +- tests/test_badblocks.toml | 1390 +++++++++----- tests/test_ck.toml | 570 ++++-- tests/test_exhaustion.toml | 545 ++++-- tests/test_forphans.toml | 3553 +++++++++++++++++++++++++++++++---- tests/test_gc.toml | 462 +++-- tests/test_grow.toml | 291 ++- tests/test_relocations.toml | 466 +++-- tests/test_traversal.toml | 462 +++-- 10 files changed, 6177 insertions(+), 1693 deletions(-) diff --git a/lfs.c b/lfs.c index 79023296..0fcb678a 100644 --- a/lfs.c +++ b/lfs.c @@ -9311,9 +9311,11 @@ static int lfsr_mtree_pathlookup(lfs_t *lfs, const char **path, // only continue if we hit a directory if (tag != LFSR_TAG_DIR) { - return (tag == LFSR_TAG_STICKYNOTE) + return (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) ? LFS_ERR_NOENT - : (tag == LFSR_TAG_REG) + : (tag == LFSR_TAG_REG || tag == LFSR_TAG_STICKYNOTE) ? LFS_ERR_NOTDIR : LFS_ERR_NOTSUP; } @@ -10176,9 +10178,14 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { if (err && !(err == LFS_ERR_NOENT && lfsr_path_islast(path))) { return err; } - // already exists? stickynotes don't really exist + // TODO LFSR_TAG_ORPHAN maybe? + // already exists? if we find a stickynote, check to see if there + // are any open in-sync file handles to decide if it really exists bool exists = (err != LFS_ERR_NOENT); - if (exists && tag != LFSR_TAG_STICKYNOTE) { + if (exists + && (tag != LFSR_TAG_STICKYNOTE + || lfsr_omdir_ismidopen(lfs, mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC)))) { return LFS_ERR_EXIST; } @@ -10403,12 +10410,17 @@ int lfsr_remove(lfs_t *lfs, const char *path) { if (err) { return err; } - // stickynotes don't really exist - if (tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { return LFS_ERR_NOENT; } // we can't remove unknown types or else we may leak resources - if (tag != LFSR_TAG_REG && tag != LFSR_TAG_DIR) { + if (tag != LFSR_TAG_REG + && tag != LFSR_TAG_DIR + && tag != LFSR_TAG_STICKYNOTE) { return LFS_ERR_NOTSUP; } @@ -10526,12 +10538,17 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { if (err) { return err; } - // stickynotes don't really exist - if (old_tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (old_tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, old_mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { return LFS_ERR_NOENT; } // we can't rename unknown types or else we may leak resources - if (old_tag != LFSR_TAG_REG && old_tag != LFSR_TAG_DIR) { + if (old_tag != LFSR_TAG_REG + && old_tag != LFSR_TAG_DIR + && old_tag != LFSR_TAG_STICKYNOTE) { return LFS_ERR_NOTSUP; } @@ -10571,15 +10588,26 @@ int lfsr_rename(lfs_t *lfs, const char *old_path, const char *new_path) { return LFS_ERR_INVAL; } - // renaming different types is an error - // - // unless we found a stickynote, these don't really exist - if (old_tag != new_tag && new_tag != LFSR_TAG_STICKYNOTE) { - return (new_tag == LFSR_TAG_DIR) - ? LFS_ERR_ISDIR - : (new_tag == LFSR_TAG_REG) - ? LFS_ERR_NOTDIR - : LFS_ERR_NOTSUP; + // we allow reg <-> stickynote renaming, but renaming a non-dir + // to a dir and a dir to a non-dir is an error + if (old_tag != LFSR_TAG_DIR && new_tag == LFSR_TAG_DIR) { + return LFS_ERR_ISDIR; + } + if (old_tag == LFSR_TAG_DIR + && new_tag != LFSR_TAG_DIR + // if we find a stickynote, check to see if there are + // any open in-sync file handles to decide if it really + // exists + && (new_tag != LFSR_TAG_STICKYNOTE + || lfsr_omdir_ismidopen(lfs, new_mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC)))) { + return LFS_ERR_NOTDIR; + } + // we can't rename unknown types or else we may leak resources + if (new_tag != LFSR_TAG_REG + && new_tag != LFSR_TAG_DIR + && new_tag != LFSR_TAG_STICKYNOTE) { + return LFS_ERR_NOTSUP; } // renaming to ourself is a noop @@ -10743,8 +10771,11 @@ int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { if (err) { return err; } - // stickynotes don't really exist - if (tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { return LFS_ERR_NOENT; } @@ -10780,8 +10811,11 @@ int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) { if (err) { return err; } - // stickynotes don't really exist - if (tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { return LFS_ERR_NOENT; } @@ -10884,8 +10918,11 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) { return LFS_ERR_NOENT; } - // skip stickynotes, we pretend these don't exist - if (tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, dir->o.mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { dir->o.mdir.mid += 1; dir->pos += 1; continue; @@ -10997,8 +11034,11 @@ static int lfsr_lookupattr(lfs_t *lfs, const char *path, uint8_t type, if (err) { return err; } - // stickynotes don't really exist - if (tag == LFSR_TAG_STICKYNOTE) { + // if we find a stickynote, check to see if there are any open + // in-sync file handles to decide if it really exists + if (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, mdir_->mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { return LFS_ERR_NOENT; } @@ -11333,8 +11373,13 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, } bool exists = err != LFS_ERR_NOENT; - // creating a new entry? - if (!exists || tag == LFSR_TAG_STICKYNOTE) { + // creating a new entry? if we find a stickynote, check to see if + // there are any open in-sync file handles to decide if it really + // exists + if (!exists + || (tag == LFSR_TAG_STICKYNOTE + && !lfsr_omdir_ismidopen(lfs, file->b.o.mdir.mid, + ~(LFS_o_ZOMBIE | LFS_O_DESYNC)))) { if (!lfsr_o_iscreat(flags)) { return LFS_ERR_NOENT; } @@ -11345,19 +11390,6 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, return LFS_ERR_NOTDIR; } - // 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_ismidopen(lfs, file->b.o.mdir.mid, - ~(LFS_o_ZOMBIE | LFS_O_DESYNC))) { - return LFS_ERR_EXIST; - } - // create a stickynote entry if we don't have one, this reserves the // mid until first sync if (!exists) { @@ -11385,11 +11417,6 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, } } } - - // mark as uncreated and unsynced, we need to convert to reg file - // on first sync - file->b.o.flags |= LFS_o_UNCREAT | LFS_o_UNSYNC; - } else { // wanted to create a new entry? if (lfsr_o_isexcl(flags)) { @@ -11397,13 +11424,19 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, } // wrong type? - if (tag != LFSR_TAG_REG) { + if (tag != LFSR_TAG_REG && tag != LFSR_TAG_STICKYNOTE) { return (tag == LFSR_TAG_DIR) ? LFS_ERR_ISDIR : LFS_ERR_NOTSUP; } } + // if stickynote, mark as uncreated and unsynced, we need to convert + // to reg file on first sync + if (!exists || tag == LFSR_TAG_STICKYNOTE) { + file->b.o.flags |= LFS_o_UNCREAT | LFS_o_UNSYNC; + } + // allocate cache if necessary if (file->cfg->cache_buffer) { file->cache.buffer = file->cfg->cache_buffer; diff --git a/lfs.h b/lfs.h index 1291df05..3a11928d 100644 --- a/lfs.h +++ b/lfs.h @@ -113,10 +113,10 @@ enum lfs_type { // file types LFS_TYPE_REG = 1, LFS_TYPE_DIR = 2, + LFS_TYPE_STICKYNOTE = 5, // internally used types, don't use these LFS_TYPE_BOOKMARK = 4, - LFS_TYPE_STICKYNOTE = 5, LFS_TYPE_TRAVERSAL = 9, }; diff --git a/tests/test_badblocks.toml b/tests/test_badblocks.toml index a63399d3..42c3d17e 100644 --- a/tests/test_badblocks.toml +++ b/tests/test_badblocks.toml @@ -1016,11 +1016,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -1043,28 +1044,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -1073,15 +1071,47 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -1093,26 +1123,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -1120,22 +1151,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -1143,6 +1166,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1150,12 +1176,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1165,11 +1220,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -1179,11 +1241,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -1195,6 +1252,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -1206,12 +1271,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -1219,6 +1287,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -1227,8 +1297,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -1245,13 +1318,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } } @@ -1262,8 +1328,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -1282,8 +1353,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -1301,8 +1377,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -1323,12 +1403,14 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfsr_unmount(&lfs) => 0; + // reset badblock lfs_emubd_markgood(CFG, badblock) => 0; } @@ -1387,12 +1469,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -1415,31 +1498,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -1448,15 +1528,50 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -1468,29 +1583,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -1498,22 +1611,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -1521,6 +1626,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1528,12 +1636,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1543,11 +1682,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -1559,11 +1705,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -1575,31 +1716,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -1607,6 +1768,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -1617,11 +1780,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -1639,52 +1805,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } } @@ -1697,6 +1863,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1721,6 +1891,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1750,8 +1924,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -1773,6 +1951,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -2799,11 +2979,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -2826,28 +3007,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -2856,12 +3034,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -2875,26 +3086,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -2902,22 +3114,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -2925,6 +3129,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -2932,12 +3139,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -2947,11 +3183,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -2961,11 +3204,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -2977,6 +3215,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -2988,12 +3234,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -3001,6 +3250,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -3009,8 +3260,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -3027,13 +3281,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } } @@ -3044,8 +3291,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -3064,8 +3316,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -3083,8 +3340,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -3105,6 +3366,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -3171,12 +3433,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -3199,31 +3462,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -3232,12 +3492,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -3251,29 +3547,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -3281,22 +3575,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -3304,6 +3590,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -3311,12 +3600,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -3326,11 +3646,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -3342,11 +3669,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -3358,31 +3680,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -3390,6 +3732,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -3400,11 +3744,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -3422,52 +3769,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } } @@ -3480,6 +3827,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -3504,6 +3855,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -3517,7 +3872,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -3532,8 +3888,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -3555,6 +3915,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -4574,11 +4936,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -4601,28 +4964,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -4631,12 +4991,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -4650,26 +5043,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -4677,22 +5071,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -4700,6 +5086,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -4707,12 +5096,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -4722,11 +5140,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -4736,11 +5161,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -4752,6 +5172,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -4763,12 +5191,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -4776,6 +5207,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -4784,8 +5217,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -4802,13 +5238,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } } @@ -4819,8 +5248,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -4839,8 +5273,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -4858,8 +5297,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -4880,6 +5323,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -4946,12 +5390,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -4974,31 +5419,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -5007,12 +5449,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -5026,29 +5504,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -5056,22 +5532,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -5079,6 +5547,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -5086,12 +5557,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -5101,11 +5603,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -5117,11 +5626,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -5133,31 +5637,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -5165,6 +5689,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -5175,11 +5701,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -5197,52 +5726,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } } @@ -5255,6 +5784,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -5279,6 +5812,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -5292,7 +5829,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -5307,8 +5845,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -5330,6 +5872,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); diff --git a/tests/test_ck.toml b/tests/test_ck.toml index d762d434..271c600f 100644 --- a/tests/test_ck.toml +++ b/tests/test_ck.toml @@ -3548,11 +3548,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -3639,27 +3640,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; // open the actual file char name[256]; @@ -3668,24 +3667,58 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { + free(sim_files[j]); goto corrupt_mounted; } - sim_file_count++; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); assert(d == SIZE || d == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { + if (d == LFS_ERR_NOSPC) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); goto corrupt_mounted; } } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -3697,44 +3730,12 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); - // update sim - sim_files[j]->prng = wprng; - if (!sim_files[j]->zombie) { - // insert into our sim - for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } - break; - } - } - - // update related sim files - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; - sim_files[k]->prng = wprng; - } - } - } - // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); @@ -3751,6 +3752,31 @@ code = ''' goto corrupt_mounted; } + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // update in our sim + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + // new prng + sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; + } + } + } + // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -3758,6 +3784,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -3765,12 +3794,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -3780,20 +3838,6 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; - // delete from our sim - memmove(&sim[j], &sim[j+1], - (sim_size-(j+1))*sizeof(lfs_size_t)); - memmove(&sim_prngs[j], &sim_prngs[j+1], - (sim_size-(j+1))*sizeof(uint32_t)); - sim_size -= 1; - - // mark any related sim files as zombied - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x) { - sim_files[k]->zombie = true; - } - } - // delete this file char name[256]; sprintf(name, "batman%03x", x); @@ -3803,6 +3847,22 @@ code = ''' goto corrupt_mounted; } + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); + sim_size -= 1; + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } + // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -3814,6 +3874,18 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } // update our sim for (lfs_size_t k = 0;; k++) { @@ -3825,12 +3897,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -3838,6 +3913,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -3846,8 +3923,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -3864,17 +3944,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - int err = lfsr_rename(&lfs, old_name, new_name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto corrupt_mounted; - } } } @@ -3885,8 +3954,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -3905,8 +3979,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -3924,14 +4003,19 @@ code = ''' } uint8_t rbuf[SIZE]; - lfs_ssize_t d = lfsr_file_read(&lfs, &file, rbuf, SIZE); - assert(d == SIZE - || (d == LFS_ERR_CORRUPT && (METHOD == 2 || METHOD == 3))); - if (d == LFS_ERR_CORRUPT) { - lfsr_file_close(&lfs, &file) => 0; - goto corrupt_mounted; + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfs_ssize_t d = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(d == SIZE + || (d == LFS_ERR_CORRUPT + && (METHOD == 2 || METHOD == 3))); + if (d == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); } - assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } @@ -3947,7 +4031,8 @@ code = ''' uint8_t rbuf[SIZE]; lfs_ssize_t d = lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE); assert(d == SIZE - || (d == LFS_ERR_CORRUPT && (METHOD == 2 || METHOD == 3))); + || (d == LFS_ERR_CORRUPT + && (METHOD == 2 || METHOD == 3))); if (d == LFS_ERR_CORRUPT) { goto corrupt_mounted; } @@ -3958,6 +4043,7 @@ corrupt_mounted:; // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; @@ -4054,12 +4140,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -4146,30 +4233,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; // open the actual file char name[256]; @@ -4178,24 +4263,61 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { + free(sim_files[j]); goto corrupt_mounted; } - sim_file_count++; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); assert(d == SIZE || d == LFS_ERR_NOSPC); if (d == LFS_ERR_NOSPC) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); goto corrupt_mounted; } } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -4207,47 +4329,12 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); - // update sim - sim_files[j]->prng = wprng; - if (!sim_files[j]->zombie) { - // insert into our sim - for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } - break; - } - } - - // update related sim files - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; - sim_files[k]->prng = wprng; - } - } - } - // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); @@ -4264,6 +4351,31 @@ code = ''' goto corrupt_mounted; } + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // update in our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + // new prng + sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; + } + } + } + // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -4271,6 +4383,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -4278,12 +4393,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -4293,11 +4439,22 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -4309,15 +4466,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - int err = lfsr_remove(&lfs, name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto corrupt_mounted; - } - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -4329,31 +4477,55 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto corrupt_mounted; + } // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -4361,6 +4533,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -4371,11 +4545,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -4393,52 +4570,21 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - int err = lfsr_rename(&lfs, old_name, new_name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto corrupt_mounted; - } - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } - // mark any related sim files as zombied - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x) { - sim_files[k]->zombie = true; - } - } - // make the directory char name[256]; sprintf(name, "batman%03x", x); @@ -4447,6 +4593,33 @@ code = ''' if (err == LFS_ERR_NOSPC) { goto corrupt_mounted; } + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } } } @@ -4459,6 +4632,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -4483,6 +4660,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -4512,14 +4693,19 @@ code = ''' } uint8_t rbuf[SIZE]; - lfs_ssize_t d = lfsr_file_read(&lfs, &file, rbuf, SIZE); - assert(d == SIZE - || (d == LFS_ERR_CORRUPT && (METHOD == 2 || METHOD == 3))); - if (d == LFS_ERR_CORRUPT) { - lfsr_file_close(&lfs, &file) => 0; - goto corrupt_mounted; + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfs_ssize_t d = lfsr_file_read(&lfs, &file, rbuf, SIZE); + assert(d == SIZE + || (d == LFS_ERR_CORRUPT + && (METHOD == 2 || METHOD == 3))); + if (d == LFS_ERR_CORRUPT) { + lfsr_file_close(&lfs, &file) => 0; + goto corrupt_mounted; + } + assert(memcmp(rbuf, wbuf, SIZE) == 0); } - assert(memcmp(rbuf, wbuf, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; } } @@ -4536,7 +4722,8 @@ code = ''' uint8_t rbuf[SIZE]; lfs_ssize_t d = lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE); assert(d == SIZE - || (d == LFS_ERR_CORRUPT && (METHOD == 2 || METHOD == 3))); + || (d == LFS_ERR_CORRUPT + && (METHOD == 2 || METHOD == 3))); if (d == LFS_ERR_CORRUPT) { goto corrupt_mounted; } @@ -4547,6 +4734,8 @@ corrupt_mounted:; // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; @@ -4554,6 +4743,7 @@ corrupt_mounted:; } free(sim_files); lfsr_unmount(&lfs) => 0; + // how many errors did we survive? printf("survived %d block errors!\n", (int)(i/PERIOD)); ''' diff --git a/tests/test_exhaustion.toml b/tests/test_exhaustion.toml index 17dd02c6..66f25856 100644 --- a/tests/test_exhaustion.toml +++ b/tests/test_exhaustion.toml @@ -754,11 +754,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -781,27 +782,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; // open the actual file char name[256]; @@ -810,24 +809,58 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { + free(sim_files[j]); goto dead; } - sim_file_count++; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); assert(d == SIZE || d == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { + if (d == LFS_ERR_NOSPC) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); goto dead; } } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -839,44 +872,12 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); - // update sim - sim_files[j]->prng = wprng; - if (!sim_files[j]->zombie) { - // insert into our sim - for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } - break; - } - } - - // update related sim files - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; - sim_files[k]->prng = wprng; - } - } - } - // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); @@ -891,6 +892,31 @@ code = ''' goto dead; } + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // update in our sim + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + // new prng + sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; + } + } + } + // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -898,6 +924,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -905,12 +934,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -920,20 +978,6 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; - // delete from our sim - memmove(&sim[j], &sim[j+1], - (sim_size-(j+1))*sizeof(lfs_size_t)); - memmove(&sim_prngs[j], &sim_prngs[j+1], - (sim_size-(j+1))*sizeof(uint32_t)); - sim_size -= 1; - - // mark any related sim files as zombied - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x) { - sim_files[k]->zombie = true; - } - } - // delete this file char name[256]; sprintf(name, "batman%03x", x); @@ -943,6 +987,22 @@ code = ''' goto dead; } + // delete from our sim + memmove(&sim[j], &sim[j+1], + (sim_size-(j+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[j], &sim_prngs[j+1], + (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); + sim_size -= 1; + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } + // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -954,6 +1014,18 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto dead; + } // update our sim for (lfs_size_t k = 0;; k++) { @@ -965,12 +1037,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -978,6 +1053,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -986,8 +1063,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -1004,17 +1084,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - int err = lfsr_rename(&lfs, old_name, new_name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto dead; - } } // check our simulation every power-of-2 ops @@ -1026,8 +1095,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -1046,8 +1120,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -1065,8 +1144,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -1080,8 +1163,8 @@ code = ''' lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &sim_files[j]->file, - rbuf, SIZE) => SIZE; + lfsr_file_read(&lfs, &sim_files[j]->file, rbuf, SIZE) + => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } @@ -1091,6 +1174,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; @@ -1176,12 +1260,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -1204,30 +1289,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; // open the actual file char name[256]; @@ -1236,24 +1319,61 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { + free(sim_files[j]); goto dead; } - sim_file_count++; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); assert(d == SIZE || d == LFS_ERR_NOSPC); if (d == LFS_ERR_NOSPC) { + lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + free(sim_files[j]); goto dead; } } + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -1265,47 +1385,12 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); - // update sim - sim_files[j]->prng = wprng; - if (!sim_files[j]->zombie) { - // insert into our sim - for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } - break; - } - } - - // update related sim files - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; - sim_files[k]->prng = wprng; - } - } - } - // write to the file lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs_ssize_t d = lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE); @@ -1320,6 +1405,31 @@ code = ''' goto dead; } + // update sim + sim_files[j]->prng = wprng; + if (!sim_files[j]->zombie) { + // update in our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; + break; + } + } + + // update related sim files + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + // new prng + sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; + } + } + } + // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -1327,6 +1437,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1334,12 +1447,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1349,11 +1493,22 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + int err = lfsr_remove(&lfs, name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto dead; + } + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -1365,15 +1520,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - int err = lfsr_remove(&lfs, name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto dead; - } - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -1385,31 +1531,55 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + int err = lfsr_rename(&lfs, old_name, new_name); + assert(!err || err == LFS_ERR_NOSPC); + if (err == LFS_ERR_NOSPC) { + goto dead; + } // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -1417,6 +1587,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -1427,11 +1599,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -1449,52 +1624,21 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - int err = lfsr_rename(&lfs, old_name, new_name); - assert(!err || err == LFS_ERR_NOSPC); - if (err == LFS_ERR_NOSPC) { - goto dead; - } - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } - // mark any related sim files as zombied - for (lfs_size_t k = 0; k < sim_file_count; k++) { - if (sim_files[k]->x == x) { - sim_files[k]->zombie = true; - } - } - // make the directory char name[256]; sprintf(name, "batman%03x", x); @@ -1503,6 +1647,33 @@ code = ''' if (err == LFS_ERR_NOSPC) { goto dead; } + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + + // mark any related sim files as zombied + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x) { + sim_files[k]->zombie = true; + } + } } // check our simulation every power-of-2 ops @@ -1516,6 +1687,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1540,6 +1715,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1569,8 +1748,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -1596,6 +1779,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; diff --git a/tests/test_forphans.toml b/tests/test_forphans.toml index 38e2a719..5e703581 100644 --- a/tests/test_forphans.toml +++ b/tests/test_forphans.toml @@ -2,7 +2,8 @@ after = ['test_fwrite', 'test_fsync'] -# Some specific tests + +# test files don't exist until first sync/close [cases.test_forphans_uncreat] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -32,7 +33,7 @@ code = ''' // check that the file doesn't _really_ exist lfs_t lfs_; - lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0; // via stat struct lfs_info info; lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT; @@ -69,7 +70,7 @@ code = ''' } // check that the file still doesn't _really_ exist - lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0; // via stat lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT; // via readdir @@ -94,7 +95,7 @@ code = ''' lfsr_file_sync(&lfs, &file) => 0; // now it should show up - lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0; // via stat lfsr_stat(&lfs_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); @@ -136,7 +137,7 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; // now it should show up - lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0; // via stat lfsr_stat(&lfs_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); @@ -176,7 +177,7 @@ code = ''' lfsr_unmount(&lfs) => 0; // should still be there - lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + lfsr_mount(&lfs_, LFS_M_RDONLY, CFG) => 0; // via stat lfsr_stat(&lfs_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); @@ -214,6 +215,8 @@ code = ''' lfsr_unmount(&lfs_) => 0; ''' + +# test files don't exist until first sync/close, even under powerloss [cases.test_forphans_uncreat_pl] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -291,6 +294,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# test files don't exist until first sync/close, even under powerloss [cases.test_forphans_uncreat_many_pl] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -378,6 +382,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' +# test files only exist as stickynotes until first sync/close [cases.test_forphans_uncreat_sync_wr] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -405,10 +410,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // uncommitted files appear as stickynotes // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -420,14 +428,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; // write to the file uint32_t prng = 42; @@ -443,9 +455,12 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; @@ -456,13 +471,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } // but we should still recieve sync broadcasts on sync/close @@ -616,10 +636,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // uncommitted files appear as stickynotes // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -631,14 +654,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; // write to the second file uint32_t prng = 42; @@ -654,9 +681,12 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; @@ -667,13 +697,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } if (SYNC) { @@ -786,7 +821,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_uncreat_desync_wdwr] +[cases.test_forphans_uncreat_wdwr] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', @@ -835,10 +870,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // uncommitted files appear as stickynotes // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -850,14 +888,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } // write to the second file @@ -874,10 +916,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // uncommitted files appear as stickynotes // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -889,14 +934,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } if (SYNC) { @@ -1068,6 +1117,7 @@ code = ''' // note we must sync to clear the desync flag lfsr_file_close(&lfs, &file) => 0; + // now it should show up // via stat lfsr_stat(&lfs, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); @@ -1237,12 +1287,8 @@ code = ''' // attempt to create a new file over the uncreat // - // counterintuitively, we _do_ error on an attempt to create an excl - // file when there is an uncreat, even though the file doesn't exist - // yet - // - // otherwise it's easy to create the same file twice with excl, - // which isn't very useful and confusing for users + // this errors, otherwise it's easy to create the same file twice + // with excl, which would be super duper confusing for users // lfsr_file_t file; lfsr_file_open(&lfs, &file, "batman", @@ -1267,58 +1313,50 @@ code = ''' } // make sure the excl file had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { - // via stat - struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => 0; - assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); - // via readdir - 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); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); 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, "batman") == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { assert(info.type == LFS_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); - lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - // via open - lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); - lfsr_file_close(&lfs, &file_) => 0; } else { - // via stat - struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; - // via readdir - 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) => LFS_ERR_NOENT; - lfsr_dir_close(&lfs, &dir) => 0; - // via open - lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; } + lfsr_file_close(&lfs, &file_) => 0; if (CLOSE == 0) { lfsr_file_close(&lfs, &uncreat) => 0; @@ -1326,7 +1364,2435 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_uncreat_desync_open] +[cases.test_forphans_uncreat_mkdir] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // attempt to mkdir + // + // this should fail because of the stickynote + // + lfsr_mkdir(&lfs, "batman") => LFS_ERR_EXIST; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the excl file had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can remove stickynotes +[cases.test_forphans_uncreat_rm] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // remove the stickynote + lfsr_remove(&lfs, "batman") => 0; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // the stickynote should be gone + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => LFS_ERR_NOENT; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can rename stickynotes +[cases.test_forphans_uncreat_mv] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename the stickynote + lfsr_rename(&lfs, "batman", "catman") => 0; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the stickynote survived the rename + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "catman", &info) => 0; + assert(strcmp(info.name, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can rename stickynotes onto files +[cases.test_forphans_uncreat_mv_replace] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreate + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + // create another file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "catman", + LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfsr_file_write(&lfs, &file, "catman!", strlen("catman!")) + => strlen("catman!"); + lfsr_file_close(&lfs, &file) => 0; + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename the stickynote + lfsr_rename(&lfs, "batman", "catman") => 0; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the stickynote survived the rename + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "catman", &info) => 0; + assert(strcmp(info.name, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can rename stickynotes onto other files +[cases.test_forphans_uncreat_mv_src_file] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreate + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "catman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, "catman!", strlen("catman!")) + => strlen("catman!"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename the stickynote + lfsr_rename(&lfs, "batman", "catman") => 0; + + // we should still be able to read our uncreate/file + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + lfsr_file_rewind(&lfs, &file) => 0; + lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) + => strlen("catman!"); + assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &file) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the stickynote survived the rename + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "catman", &info) => 0; + assert(strcmp(info.name, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &file) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can rename files onto stickynotes +[cases.test_forphans_uncreat_mv_dst_file] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreate + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "catman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, "catman!", strlen("catman!")) + => strlen("catman!"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename onto the stickynote + lfsr_rename(&lfs, "catman", "batman") => 0; + + // we should still be able to read our uncreate/file + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + lfsr_file_rewind(&lfs, &file) => 0; + lfsr_file_read(&lfs, &file, rbuf, sizeof(rbuf)) + => strlen("catman!"); + assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &file) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // test that the file replaced the stickynote + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("catman!")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("catman!")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("catman!"); + assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &file) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test we can rename stickynotes onto other stickynotes +[cases.test_forphans_uncreat_mv_stickynote] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreate + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + // create another uncreate + lfsr_file_t uncreat_; + lfsr_file_open(&lfs, &uncreat_, "catman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat_, "WoO~", strlen("WoO~")) + => strlen("WoO~"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename the stickynote + lfsr_rename(&lfs, "batman", "catman") => 0; + + // we should still be able to read our uncreates + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + lfsr_file_rewind(&lfs, &uncreat_) => 0; + lfsr_file_read(&lfs, &uncreat_, rbuf, sizeof(rbuf)) + => strlen("WoO~"); + assert(memcmp(rbuf, "WoO~", strlen("WoO~")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &uncreat_) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the stickynote survived the rename + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "catman", &info) => 0; + assert(strcmp(info.name, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "catman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "catman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + lfsr_file_close(&lfs, &uncreat_) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +# test renaming a stickynote onto itself does nothing +[cases.test_forphans_uncreat_mv_noop] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreate + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // rename the stickynote + lfsr_rename(&lfs, "batman", "batman") => 0; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the stickynote survived the rename + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_uncreat_not_dir] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // attempt to rename a dir onto the stickynote + // + // this should fail because of the stickynote + // + lfsr_mkdir(&lfs, "datman") => 0; + lfsr_rename(&lfs, "datman", "batman") => LFS_ERR_NOTDIR; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the mkdir had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_stat(&lfs, "datman", &info) => 0; + assert(strcmp(info.name, "datman") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "datman") == 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; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_uncreat_not_stickynote] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // attempt to rename the stickynote onto a dir + // + // this should fail because of the stickynote + // + lfsr_mkdir(&lfs, "datman") => 0; + lfsr_rename(&lfs, "batman", "datman") => LFS_ERR_ISDIR; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the mkdir had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_stat(&lfs, "datman", &info) => 0; + assert(strcmp(info.name, "datman") == 0); + assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => 0; + assert(strcmp(info.name, "datman") == 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; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_uncreat_not_root] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // attempt to rename the stickynote onto the root + // + // this should fail because of the stickynote + // + // well, because of the root really, but also because of the + // stickynote + // + lfsr_rename(&lfs, "batman", "/") => LFS_ERR_INVAL; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the mkdir had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_uncreat_not_noent] +# CLOSE=0 => don't close (before end of test) +# CLOSE=1 => close after op +defines.CLOSE = [0, 1] +# REMOUNT=0 => don't remount +# REMOUNT=1 => remount after op +defines.REMOUNT = [0, 1] +defines.MKCONSISTENT = [false, true] +if = 'REMOUNT <= CLOSE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create an uncreat + lfsr_file_t uncreat; + lfsr_file_open(&lfs, &uncreat, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &uncreat, + "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) + => strlen("WoOoOoOoOoO"); + + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // attempt to rename the stickynote onto an invalid path + // + // this should fail because of the stickynote + // + // well, because of the invalid path really, but also because of the + // stickynote + // + lfsr_rename(&lfs, "batman", "no/batman") => LFS_ERR_NOENT; + + // we should still be able to read our uncreat + lfsr_file_rewind(&lfs, &uncreat) => 0; + uint8_t rbuf[256]; + lfsr_file_read(&lfs, &uncreat, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + + if (CLOSE == 1) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + if (REMOUNT == 1) { + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // make sure the mkdir had no effect + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + // via readdir + 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, "batman") == 0); + if (CLOSE == 1) { + assert(info.type == LFS_TYPE_REG); + assert(info.size == strlen("WoOoOoOoOoO")); + } else { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + if (CLOSE == 1) { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) + => strlen("WoOoOoOoOoO"); + assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); + } else { + lfsr_file_read(&lfs, &file_, rbuf, sizeof(rbuf)) => 0; + } + lfsr_file_close(&lfs, &file_) => 0; + + if (CLOSE == 0) { + lfsr_file_close(&lfs, &uncreat) => 0; + } + lfsr_unmount(&lfs) => 0; +''' + + +# test desync files don't exist until first sync + close +[cases.test_forphans_undesync] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 'LFS_MIN(64, SIZE)' +defines.SYNC = [false, true] +defines.MKCONSISTENT = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a desync file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "batman", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // check that the file doesn't _really_ exist + lfs_t lfs_; + lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + // via stat + struct lfs_info info; + lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs_, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfsr_unmount(&lfs_) => 0; + + // write to the file + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // check that the file still doesn't _really_ exist + lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + // via stat + lfsr_stat(&lfs_, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs_, &dir) => 0; + // via open + lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + lfsr_unmount(&lfs_) => 0; + } + + if (SYNC) { + // sync the file + lfsr_file_sync(&lfs, &file) => 0; + + // now it should show up + lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + // via stat + lfsr_stat(&lfs_, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs_, &dir) => 0; + // via open + lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs_, &file_) => 0; + lfsr_unmount(&lfs_) => 0; + } + + // sync the file + lfsr_file_sync(&lfs, &file) => 0; + // close the file + lfsr_file_close(&lfs, &file) => 0; + + // now it should show up + lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + // via stat + lfsr_stat(&lfs_, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs_, &dir) => 0; + // via open + lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs_, &file_) => 0; + lfsr_unmount(&lfs_) => 0; + + lfsr_unmount(&lfs) => 0; + + // should still be there + lfsr_mount(&lfs_, LFS_M_RDWR, CFG) => 0; + // via stat + lfsr_stat(&lfs_, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs_, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs_, &dir) => 0; + // via open + lfsr_file_open(&lfs_, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs_, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs_, &file_) => 0; + lfsr_unmount(&lfs_) => 0; +''' + +# test desync files don't exist until first sync + close, even under powerloss +[cases.test_forphans_undesync_pl] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 'LFS_MIN(64, SIZE)' +reentrant = true +code = ''' + // format once per test + lfs_t lfs; + int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); + if (err) { + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + + // create a desync file + // + // note the excl flag + lfsr_file_t file; + err = lfsr_file_open(&lfs, &file, "batman", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC); + assert(!err || err == LFS_ERR_EXIST); + + if (err != LFS_ERR_EXIST) { + // write to the file + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK; + } + + // sync the file + lfsr_file_sync(&lfs, &file) => 0; + // close the file + lfsr_file_close(&lfs, &file) => 0; + } + + // we should be able to read the file now + lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0; + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + + // and after remounting + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + lfsr_file_open(&lfs, &file, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# test desync files don't exist until first sync + close, even under powerloss +[cases.test_forphans_undesync_many_pl] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', +] +defines.CHUNK = 8 +defines.N = 128 +reentrant = true +code = ''' + // format once per test + lfs_t lfs; + int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); + if (err) { + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + } + + // create N desync files + lfsr_file_t files[N]; + bool exists[N]; + for (lfs_size_t j = 0; j < N; j++) { + // note the excl flag + char name[256]; + sprintf(name, "batman%03x", j); + err = lfsr_file_open(&lfs, &files[j], name, + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC); + assert(!err || err == LFS_ERR_EXIST); + + exists[j] = (err == LFS_ERR_EXIST); + if (!exists[j]) { + // write to the file + uint32_t prng = 42 + j; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &files[j], wbuf, CHUNK) => CHUNK; + } + } + } + // sync and close + for (lfs_size_t j = 0; j < N; j++) { + if (!exists[j]) { + // sync the file + lfsr_file_sync(&lfs, &files[j]) => 0; + // close the file + lfsr_file_close(&lfs, &files[j]) => 0; + } + } + + // we should be able to read the files now + for (lfs_size_t j = 0; j < N; j++) { + char name[256]; + sprintf(name, "batman%03x", j); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + uint32_t prng = 42 + j; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + } + + // and after remounting + lfsr_unmount(&lfs) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + for (lfs_size_t j = 0; j < N; j++) { + char name[256]; + sprintf(name, "batman%03x", j); + lfsr_file_t file; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => 0; + uint32_t prng = 42 + j; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + } + + lfsr_unmount(&lfs) => 0; +''' + +# test desync files aren't even openable until first sync + close +[cases.test_forphans_undesync_sync_wr] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 'LFS_MIN(64, SIZE)' +defines.SYNC = [false, true] +defines.MKCONSISTENT = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a desync file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "batman", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // as far as the filesystem is concerned, the file does not exist yet + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + + // write to the file + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // as far as the filesystem is concerned, the file does not exist yet + // via stat + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + } + + // 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) => 0; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + if (SYNC) { + // sync the file + lfsr_file_sync(&lfs, &file) => 0; + + // now it should show up + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file__) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + } + + // sync the file + lfsr_file_sync(&lfs, &file) => 0; + // close the file + lfsr_file_close(&lfs, &file) => 0; + + // now it should show up + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file__) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file__, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + + lfsr_file_close(&lfs, &file__) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_undesync_sync_rw] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 'LFS_MIN(64, SIZE)' +defines.SYNC = [false, true] +defines.MKCONSISTENT = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + // open a second desync reference + lfsr_file_t file__; + lfsr_file_open(&lfs, &file__, "batman", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_DESYNC) => 0; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // uncommitted files appear as stickynotes + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + + // write to the second file + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // still appears as a stickynote + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + } + + if (SYNC) { + // sync the second file + lfsr_file_sync(&lfs, &file__) => 0; + + // now it should show up + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + } + + // sync the second file + lfsr_file_sync(&lfs, &file__) => 0; + // close the second file + lfsr_file_close(&lfs, &file__) => 0; + + // now it should show up + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + + lfsr_file_close(&lfs, &file) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_undesync_wdwr] +defines.SIZE = [ + 'FILE_CACHE_SIZE/2', + '2*FILE_CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 'LFS_MIN(64, SIZE)' +defines.SYNC = [false, true] +defines.MKCONSISTENT = [false, true] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; + lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; + + // create a desync file + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "batman", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; + // open a second desync reference + lfsr_file_t file__; + lfsr_file_open(&lfs, &file__, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; + // and a third for checking sync broadcasts + lfsr_file_t file___; + lfsr_file_open(&lfs, &file___, "batman", + LFS_O_RDWR | LFS_O_CREAT) => 0; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // write to the first file + uint32_t prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file, wbuf, CHUNK) => CHUNK; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // still appears as a stickynote + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + } + + // write to the second file + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // still appears as a stickynote + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + } + + if (SYNC) { + // sync the second file + lfsr_file_sync(&lfs, &file__) => 0; + + // now it should show up + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file___) => 0; + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + } + + // sync the second file + lfsr_file_sync(&lfs, &file__) => 0; + // close the second file + lfsr_file_close(&lfs, &file__) => 0; + + // now it should show up + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file___) => 0; + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + + // now sync the first file, this should overwrite what is written + lfsr_file_sync(&lfs, &file) => 0; + + // now it should show up + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file___) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + + // and close, this shouldn't change anything + // + // note we must sync to clear the desync flag + lfsr_file_close(&lfs, &file) => 0; + + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + lfsr_file_close(&lfs, &file_) => 0; + + // recieved sync broadcast? + lfsr_file_rewind(&lfs, &file___) => 0; + prng = 42; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &file___, rbuf, CHUNK) => CHUNK; + assert(memcmp(wbuf, rbuf, CHUNK) == 0); + } + + lfsr_file_close(&lfs, &file___) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_forphans_undesync_open] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] @@ -1415,7 +3881,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_uncreat_desync_excl] +[cases.test_forphans_undesync_excl] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] @@ -1514,6 +3980,9 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' + + +# test that desynced, and then closed, files don't show up [cases.test_forphans_orphan] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -1690,10 +4159,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -1705,14 +4177,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } // write to the second file @@ -1729,10 +4205,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -1744,14 +4223,18 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected + // via open lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; } if (SYNC & 0x1) { @@ -1993,7 +4476,10 @@ code = ''' sprintf(name, "aatman%03x", o); lfsr_stat(&lfs, name, &info) => LFS_ERR_NOENT; } - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; @@ -2005,10 +4491,15 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; // via open lfsr_file_t file_; + uint8_t rbuf[CHUNK]; for (lfs_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); @@ -2018,11 +4509,9 @@ code = ''' lfsr_file_open(&lfs, &file_, name, LFS_O_WRONLY) => LFS_ERR_NOENT; lfsr_file_open(&lfs, &file_, name, LFS_O_RDWR) => LFS_ERR_NOENT; } - // rdonly rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; // but we should still be able to read our orphans for (lfs_size_t o = 0; o < N; o++) { @@ -2760,6 +5249,9 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' + + +# test that removed files never show up [cases.test_forphans_zombie] defines.SIZE = [ 'FILE_CACHE_SIZE/2', @@ -3063,27 +5555,7 @@ code = ''' // remove the file lfsr_remove(&lfs, "batman") => 0; - // create a second file - lfsr_file_t file__; - lfsr_file_open(&lfs, &file__, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - - // write to the second file - prng = 42+1; - for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { - uint8_t wbuf[CHUNK]; - for (lfs_size_t j = 0; j < CHUNK; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; - } - - // mkconsistent should have no effect - if (MKCONSISTENT) { - lfsr_fs_mkconsistent(&lfs) => 0; - } - - // as far as the filesystem is concerned, the file does not exist yet + // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs_info info; lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; @@ -3107,6 +5579,54 @@ code = ''' lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // create a second file + lfsr_file_t file__; + lfsr_file_open(&lfs, &file__, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // write to the second file + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; + } + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // still appears as a stickynote + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + // removed files can not be synced lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT; // but we should be able to sync our second file just fine @@ -3284,6 +5804,30 @@ code = ''' // remove the file lfsr_remove(&lfs, "batman") => 0; + // as far as the filesystem is concerned, this file does not exist anymore + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // create a second file lfsr_file_t file__; lfsr_file_open(&lfs, &file__, "batman", @@ -3314,12 +5858,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat - struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir - lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); @@ -3329,14 +5874,17 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected - lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // via open + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; // removed files can not be synced lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT; @@ -3525,48 +6073,7 @@ code = ''' // remove the file lfsr_remove(&lfs, "batman") => 0; - // create a second file - lfsr_file_t file__; - lfsr_file_open(&lfs, &file__, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - - // write to the second file - prng = 42+1; - for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { - uint8_t wbuf[CHUNK]; - for (lfs_size_t j = 0; j < CHUNK; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; - } - - // need to sync so we can remove - lfsr_file_sync(&lfs, &file__) => 0; - - // remove the file - lfsr_remove(&lfs, "batman") => 0; - - // create a third file - lfsr_file_t file___; - lfsr_file_open(&lfs, &file___, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; - - // write to the third file - prng = 42+2; - for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { - uint8_t wbuf[CHUNK]; - for (lfs_size_t j = 0; j < CHUNK; j++) { - wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); - } - lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK; - } - - // mkconsistent should have no effect - if (MKCONSISTENT) { - lfsr_fs_mkconsistent(&lfs) => 0; - } - - // as far as the filesystem is concerned, the file does not exist yet + // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs_info info; lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; @@ -3590,6 +6097,96 @@ code = ''' lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // create a second file + lfsr_file_t file__; + lfsr_file_open(&lfs, &file__, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // write to the second file + prng = 42+1; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file__, wbuf, CHUNK) => CHUNK; + } + + // need to sync so we can remove + lfsr_file_sync(&lfs, &file__) => 0; + + // remove the file + lfsr_remove(&lfs, "batman") => 0; + + // still doesn't exist + // via stat + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + + // create a third file + lfsr_file_t file___; + lfsr_file_open(&lfs, &file___, "batman", + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + + // write to the third file + prng = 42+2; + for (lfs_off_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &file___, wbuf, CHUNK) => CHUNK; + } + + // mkconsistent should have no effect + if (MKCONSISTENT) { + lfsr_fs_mkconsistent(&lfs) => 0; + } + + // still appears as a stickynote + // via stat + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + // via readdir + 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // via open + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; + // removed files can not be synced lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT; lfsr_file_sync(&lfs, &file__) => LFS_ERR_NOENT; @@ -3781,6 +6378,30 @@ code = ''' // remove the file lfsr_remove(&lfs, "batman") => 0; + // as far as the filesystem is concerned, this file does not exist anymore + // via stat + struct lfs_info info; + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_t file_; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // create a second file lfsr_file_t file__; lfsr_file_open(&lfs, &file__, "batman", @@ -3792,6 +6413,27 @@ code = ''' // remove the file lfsr_remove(&lfs, "batman") => 0; + // still doesn't exist + // via stat + lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + // via readdir + 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) => LFS_ERR_NOENT; + lfsr_dir_close(&lfs, &dir) => 0; + // rdonly rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; + // non-create rejected + lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // create a third file lfsr_file_t file___; lfsr_file_open(&lfs, &file___, "batman", @@ -3832,12 +6474,13 @@ code = ''' lfsr_fs_mkconsistent(&lfs) => 0; } - // as far as the filesystem is concerned, the file does not exist yet + // still appears as a stickynote // via stat - struct lfs_info info; - lfsr_stat(&lfs, "batman", &info) => LFS_ERR_NOENT; + lfsr_stat(&lfs, "batman", &info) => 0; + assert(strcmp(info.name, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); // via readdir - lfsr_dir_t dir; lfsr_dir_open(&lfs, &dir, "/") => 0; lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); @@ -3847,14 +6490,17 @@ code = ''' 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, "batman") == 0); + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; - // rdonly rejected - lfsr_file_t file_; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => LFS_ERR_NOENT; - // non-create rejected - lfsr_file_open(&lfs, &file_, "batman", LFS_O_WRONLY) => LFS_ERR_NOENT; - lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDWR) => LFS_ERR_NOENT; + // via open + uint8_t rbuf[CHUNK]; + lfsr_file_open(&lfs, &file_, "batman", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &file_, rbuf, CHUNK) => 0; + lfsr_file_close(&lfs, &file_) => 0; // removed files can not be synced lfsr_file_sync(&lfs, &file) => LFS_ERR_NOENT; @@ -4702,6 +7348,9 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' + + +# test some other operations that can make zombies [cases.test_forphans_zombify_mkdir] defines.POSTHUMOUS = [false, true] defines.CLOSE = [false, true] @@ -4713,10 +7362,10 @@ code = ''' lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; - // create a file, not a zombie yet + // create a desync file, not a zombie yet lfsr_file_t zombie; lfsr_file_open(&lfs, &zombie, "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; if (!POSTHUMOUS) { lfsr_file_write(&lfs, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) @@ -4828,10 +7477,10 @@ code = ''' lfsr_file_close(&lfs, &file) => 0; } - // create a file, not a zombie yet + // create a desync file, not a zombie yet lfsr_file_t zombie; lfsr_file_open(&lfs, &zombie, (INTERDIR) ? "a/batman" : "batman", - LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0; + LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL | LFS_O_DESYNC) => 0; if (!POSTHUMOUS) { lfsr_file_write(&lfs, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) @@ -4938,7 +7587,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_file_on_zombie_rm] +[cases.test_forphans_fileonzombie_rm] defines.DIR = [false, true] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) @@ -5044,7 +7693,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_file_on_zombie_mv_dst] +[cases.test_forphans_fileonzombie_mv_dst] defines.DIR = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] @@ -5224,6 +7873,7 @@ code = ''' ''' + # test that we actually cleanup orphans correctly [cases.test_forphans_cleanup] defines.SIZE = [ @@ -5806,9 +8456,12 @@ code = ''' -# these doesn't really involve scratch files, but we might as well test -# them here -[cases.test_forphans_mv] +# test that file handles follow mvs correctly +# +# these doesn't really involve stickynotes, but it's the same internal +# circuitry, we might as well test them here +# +[cases.test_forphans_file_mv] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', @@ -6050,7 +8703,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_mv_postmv] +[cases.test_forphans_file_mv_postmv] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', @@ -6352,7 +9005,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_mv_rwrw] +[cases.test_forphans_file_mv_file_rwrw] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', @@ -6703,7 +9356,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_mv_rwrw_postmv] +[cases.test_forphans_file_mv_rwrw_postmv] defines.SIZE = [ 'FILE_CACHE_SIZE/2', '2*FILE_CACHE_SIZE', @@ -7046,7 +9699,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_mv_mv_src] +[cases.test_forphans_file_mv_mv_src] defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] @@ -7213,7 +9866,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_file_on_zombie_mv_src] +[cases.test_forphans_file_fileonzombie_mv_src] defines.DIR = [false, true] defines.EXISTS = [false, true] defines.INTERDIR = [false, true] @@ -7403,7 +10056,7 @@ code = ''' # here we spam renames over an increasing number of files to hopefully hit # that case # -[cases.test_forphans_mv_split] +[cases.test_forphans_file_mv_split] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', @@ -7750,7 +10403,7 @@ code = ''' lfsr_unmount(&lfs) => 0; ''' -[cases.test_forphans_mv_split_backwards] +[cases.test_forphans_file_mv_split_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', @@ -8124,11 +10777,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -8151,28 +10805,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -8181,12 +10832,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -8200,26 +10884,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -8227,22 +10912,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -8250,6 +10927,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -8257,12 +10937,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -8272,11 +10981,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -8286,11 +11002,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -8302,6 +11013,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -8313,12 +11032,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -8326,6 +11048,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -8334,8 +11058,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -8352,13 +11079,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } } @@ -8369,8 +11089,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -8389,8 +11114,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -8408,8 +11138,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -8430,6 +11164,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -8463,12 +11198,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -8491,31 +11227,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -8524,12 +11257,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -8543,29 +11312,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -8573,22 +11340,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -8596,6 +11355,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -8603,12 +11365,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -8618,11 +11411,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -8634,11 +11434,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -8650,31 +11445,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -8682,6 +11497,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -8692,11 +11509,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -8714,52 +11534,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } } @@ -8772,6 +11592,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -8796,6 +11620,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -8809,7 +11637,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -8824,8 +11653,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -8847,6 +11680,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); diff --git a/tests/test_gc.toml b/tests/test_gc.toml index 8e8b88e5..9b2f9f10 100644 --- a/tests/test_gc.toml +++ b/tests/test_gc.toml @@ -2838,11 +2838,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -2865,28 +2866,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -2895,12 +2893,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -2914,26 +2945,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -2941,22 +2973,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -2964,6 +2988,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -2979,6 +3006,33 @@ code = ''' sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -2988,11 +3042,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -3002,11 +3063,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -3018,6 +3074,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -3029,12 +3093,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -3042,6 +3109,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -3050,8 +3119,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -3068,13 +3140,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } // gc! @@ -3093,8 +3158,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -3113,8 +3183,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -3132,8 +3207,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -3154,6 +3233,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -3203,12 +3283,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -3231,31 +3312,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -3264,12 +3342,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -3283,29 +3397,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -3313,22 +3425,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -3336,6 +3440,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -3351,6 +3458,35 @@ code = ''' sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -3360,11 +3496,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -3376,11 +3519,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -3392,31 +3530,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -3424,6 +3582,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -3434,11 +3594,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -3456,52 +3619,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } // gc! @@ -3522,6 +3685,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -3546,6 +3713,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -3559,7 +3730,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -3574,8 +3746,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -3597,6 +3773,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); diff --git a/tests/test_grow.toml b/tests/test_grow.toml index d8ca50cc..64030407 100644 --- a/tests/test_grow.toml +++ b/tests/test_grow.toml @@ -1273,11 +1273,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -1302,18 +1303,21 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng_) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng_); + sticky = true; } lfs_size_t j = sim_file_count; @@ -1331,7 +1335,7 @@ code = ''' } // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { @@ -1349,11 +1353,35 @@ code = ''' // open in our sim sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; + sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -1388,23 +1416,13 @@ code = ''' // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -1412,8 +1430,10 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } @@ -1425,6 +1445,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng_) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1432,12 +1455,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1461,6 +1513,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -1481,6 +1535,7 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng_) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; // rename this file char old_name[256]; @@ -1503,12 +1558,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -1516,6 +1574,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -1524,8 +1584,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -1591,8 +1654,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -1611,8 +1679,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -1630,8 +1703,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -1652,8 +1729,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { - lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); } @@ -1697,12 +1774,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -1727,21 +1805,24 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng_) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { - wprng = TEST_PRNG(&prng_); + if (!exist) { + wprng = TEST_PRNG(&prng); + sticky = true; } lfs_size_t j = sim_file_count; @@ -1759,7 +1840,7 @@ code = ''' } // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { @@ -1777,11 +1858,38 @@ code = ''' // open in our sim sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; + sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; + } + } + // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { @@ -1816,26 +1924,13 @@ code = ''' // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -1843,8 +1938,10 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } @@ -1856,6 +1953,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng_) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1863,12 +1963,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1892,6 +2023,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -1914,14 +2047,15 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng_) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // type mismatch? - if (sim_isdirs[k] != isdir) { + if (sim_isdirs[k] != dir) { goto nonsense; } } @@ -1950,14 +2084,18 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -1965,6 +2103,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -1975,11 +2115,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -2021,7 +2164,7 @@ code = ''' goto grow; } - // insert into our sim, use negative numbers for dirs + // insert into our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // insert @@ -2029,6 +2172,8 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; @@ -2096,6 +2241,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -2120,6 +2269,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -2133,7 +2286,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -2148,8 +2302,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -2171,8 +2329,9 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { - lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); } diff --git a/tests/test_relocations.toml b/tests/test_relocations.toml index f2da68ed..5c084f6c 100644 --- a/tests/test_relocations.toml +++ b/tests/test_relocations.toml @@ -552,11 +552,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -579,28 +580,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -609,12 +607,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -628,26 +659,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -655,22 +687,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -678,6 +702,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -685,12 +712,41 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -700,11 +756,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -714,11 +777,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -730,6 +788,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -741,12 +807,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -754,6 +823,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -762,8 +833,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -780,13 +854,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } } @@ -797,8 +864,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -817,8 +889,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -836,8 +913,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -858,6 +939,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -892,12 +974,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -920,31 +1003,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -953,12 +1033,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -972,29 +1088,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -1002,22 +1116,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -1025,6 +1131,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -1032,12 +1141,43 @@ code = ''' // close the file without affected disk lfsr_file_desync(&lfs, &sim_files[j]->file) => 0; lfsr_file_close(&lfs, &sim_files[j]->file) => 0; + // clobber closed files to try to catch lingering references + memset(&sim_files[j]->file, 0xcc, sizeof(lfsr_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -1047,11 +1187,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -1063,11 +1210,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -1079,31 +1221,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -1111,6 +1273,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -1121,11 +1285,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -1143,52 +1310,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } } @@ -1201,6 +1368,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1225,6 +1396,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -1238,7 +1413,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -1253,8 +1429,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -1276,6 +1456,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); diff --git a/tests/test_traversal.toml b/tests/test_traversal.toml index be82e246..0f02414e 100644 --- a/tests/test_traversal.toml +++ b/tests/test_traversal.toml @@ -8505,11 +8505,12 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -8541,28 +8542,25 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = false; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -8571,12 +8569,45 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -8590,26 +8621,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { - if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - } + if (sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -8617,22 +8649,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -8640,6 +8664,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + bool sticky = sim_files[j]->sticky; + bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -8655,6 +8682,33 @@ code = ''' sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -8664,11 +8718,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied @@ -8678,11 +8739,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -8694,6 +8750,14 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; + bool sticky = sim_isstickys[j]; + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { @@ -8705,12 +8769,15 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; // just renaming } else { // first delete @@ -8718,6 +8785,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } @@ -8726,8 +8795,11 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; } break; } @@ -8744,13 +8816,6 @@ code = ''' sim_files[k]->zombie = true; } } - - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; } // step the traversal @@ -8774,8 +8839,13 @@ code = ''' struct lfs_info info; lfsr_stat(&lfs, name, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_t dir; @@ -8794,8 +8864,13 @@ code = ''' sprintf(name, "batman%03x", sim[j]); lfsr_dir_read(&lfs, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); - assert(info.type == LFS_TYPE_REG); - assert(info.size == SIZE); + if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); + } else { + assert(info.type == LFS_TYPE_REG); + assert(info.size == SIZE); + } } lfsr_dir_read(&lfs, &dir, &info) => LFS_ERR_NOENT; lfsr_dir_close(&lfs, &dir) => 0; @@ -8813,8 +8888,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } @@ -8835,6 +8914,7 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]); @@ -8876,12 +8956,13 @@ code = ''' // set up a simulation to compare against lfs_size_t *sim = malloc(N*sizeof(lfs_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); + bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs_size_t sim_size = 0; typedef struct sim_file { lfs_size_t x; - bool uncreat; + bool sticky; bool zombie; uint32_t prng; lfsr_file_t file; @@ -8913,31 +8994,28 @@ code = ''' lfs_size_t x = TEST_PRNG(&prng) % N; // already exists? - bool uncreat = true; + bool exist = true; uint32_t wprng = 0; + bool sticky = true; for (lfs_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } - uncreat = false; + exist = true; wprng = sim_prngs[j]; + sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist - if (uncreat) { + if (!exist) { wprng = TEST_PRNG(&prng); + sticky = true; } - // open in our sim lfs_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); - sim_files[j]->x = x; - sim_files[j]->uncreat = uncreat; - sim_files[j]->zombie = false; - sim_files[j]->prng = wprng; - sim_file_count++; // open the actual file char name[256]; @@ -8946,12 +9024,48 @@ code = ''' LFS_O_RDWR | LFS_O_CREAT) => 0; // write some initial data if we don't exist - if (uncreat) { + if (!exist || sticky) { uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) + => SIZE; + } + + // open in our sim + sim_files[j]->x = x; + sim_files[j]->sticky = sticky; + sim_files[j]->zombie = false; + sim_files[j]->prng = wprng; + sim_file_count++; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // already seen? + if (k < sim_size && sim[k] == x) { + // new prng + sim_prngs[k] = wprng; + } else { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = false; + } + break; } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // write/rewrite a file? @@ -8965,29 +9079,27 @@ code = ''' // choose a random seed uint32_t wprng = TEST_PRNG(&prng); + // write to the file + lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; + uint8_t wbuf[SIZE]; + uint32_t wprng_ = wprng; + for (lfs_size_t k = 0; k < SIZE; k++) { + wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); + } + lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; + lfsr_file_sync(&lfs, &sim_files[j]->file) + => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; + // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { - // insert into our sim + // update in our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { - // already seen? - if (k < sim_size && sim[k] == x) { - // new prng - sim_prngs[k] = wprng; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = wprng; - sim_isdirs[k] = false; - } + // new prng + sim_prngs[k] = wprng; + // no longer sticky + sim_isstickys[k] = false; break; } } @@ -8995,22 +9107,14 @@ code = ''' // update related sim files for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { - sim_files[k]->uncreat = false; + // new prng sim_files[k]->prng = wprng; + // no longer sticky + sim_files[k]->sticky = false; } } } - // write to the file - lfsr_file_rewind(&lfs, &sim_files[j]->file) => 0; - uint8_t wbuf[SIZE]; - for (lfs_size_t k = 0; k < SIZE; k++) { - wbuf[k] = 'a' + (TEST_PRNG(&wprng) % 26); - } - lfsr_file_write(&lfs, &sim_files[j]->file, wbuf, SIZE) => SIZE; - lfsr_file_sync(&lfs, &sim_files[j]->file) - => (!sim_files[j]->zombie) ? 0 : LFS_ERR_NOENT; - // close a file? } else if (op == 2) { if (sim_file_count == 0) { @@ -9018,6 +9122,9 @@ code = ''' } // choose a random file handle lfs_size_t j = TEST_PRNG(&prng) % sim_file_count; + lfs_size_t x = sim_files[j]->x; + lfs_size_t sticky = sim_files[j]->sticky; + lfs_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies @@ -9033,6 +9140,35 @@ code = ''' sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; + // update our sim + if (sticky && !zombie) { + // orphaned? + bool orphan = true; + for (lfs_size_t k = 0; k < sim_file_count; k++) { + if (sim_files[k]->x == x && !sim_files[k]->zombie) { + orphan = false; + } + } + + // if we were never synced, delete from sim + if (orphan) { + for (lfs_size_t k = 0;; k++) { + if (sim[k] == x) { + memmove(&sim[k], &sim[k+1], + (sim_size-(k+1))*sizeof(lfs_size_t)); + memmove(&sim_prngs[k], &sim_prngs[k+1], + (sim_size-(k+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[k], &sim_isstickys[k+1], + (sim_size-(k+1))*sizeof(bool)); + memmove(&sim_isdirs[k], &sim_isdirs[k+1], + (sim_size-(k+1))*sizeof(bool)); + sim_size -= 1; + break; + } + } + } + } + // remove a file? } else if (op == 3) { if (sim_size == 0) { @@ -9042,11 +9178,18 @@ code = ''' lfs_size_t j = TEST_PRNG(&prng) % sim_size; lfs_size_t x = sim[j]; + // delete this file + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_remove(&lfs, name) => 0; + // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; @@ -9058,11 +9201,6 @@ code = ''' } } - // delete this file - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_remove(&lfs, name) => 0; - // rename a file? } else if (op == 4) { if (sim_size == 0) { @@ -9074,31 +9212,51 @@ code = ''' lfs_size_t x = sim[j]; lfs_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; - bool isdir = sim_isdirs[j]; + bool sticky = sim_isstickys[j]; + bool dir = sim_isdirs[j]; + + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= y) { + // renaming and replacing + if (k < sim_size && sim[k] == y && x != y) { + // type mismatch? + if (sim_isdirs[k] != dir) { + goto nonsense; + } + } + break; + } + } + + // rename this file + char old_name[256]; + sprintf(old_name, "batman%03x", x); + char new_name[256]; + sprintf(new_name, "batman%03x", y); + lfsr_rename(&lfs, old_name, new_name) => 0; // update our sim for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { - // type mismatch? - if (sim_isdirs[k] != isdir) { - goto nonsense; - } - // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } - // update the prng + // update the prng/sticky/dir sim_prngs[k] = wprng; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; // just renaming } else { // first delete @@ -9106,6 +9264,8 @@ code = ''' (sim_size-(j+1))*sizeof(lfs_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); + memmove(&sim_isstickys[j], &sim_isstickys[j+1], + (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { @@ -9116,11 +9276,14 @@ code = ''' (sim_size-k)*sizeof(lfs_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; - sim_isdirs[k] = isdir; + sim_isstickys[k] = sticky; + sim_isdirs[k] = dir; } break; } @@ -9138,52 +9301,52 @@ code = ''' } } - // rename this file - char old_name[256]; - sprintf(old_name, "batman%03x", x); - char new_name[256]; - sprintf(new_name, "batman%03x", y); - lfsr_rename(&lfs, old_name, new_name) => 0; - // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs_size_t x = TEST_PRNG(&prng) % N; - // insert into our sim, use negative numbers for dirs for (lfs_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; - } else { - // insert - memmove(&sim[k+1], &sim[k], - (sim_size-k)*sizeof(lfs_size_t)); - memmove(&sim_prngs[k+1], &sim_prngs[k], - (sim_size-k)*sizeof(uint32_t)); - memmove(&sim_isdirs[k+1], &sim_isdirs[k], - (sim_size-k)*sizeof(bool)); - sim_size += 1; - sim[k] = x; - sim_prngs[k] = 0; - sim_isdirs[k] = true; } break; } } + // make the directory + char name[256]; + sprintf(name, "batman%03x", x); + lfsr_mkdir(&lfs, name) => 0; + + // insert into our sim + for (lfs_size_t k = 0;; k++) { + if (k >= sim_size || sim[k] >= x) { + // insert + memmove(&sim[k+1], &sim[k], + (sim_size-k)*sizeof(lfs_size_t)); + memmove(&sim_prngs[k+1], &sim_prngs[k], + (sim_size-k)*sizeof(uint32_t)); + memmove(&sim_isstickys[k+1], &sim_isstickys[k], + (sim_size-k)*sizeof(bool)); + memmove(&sim_isdirs[k+1], &sim_isdirs[k], + (sim_size-k)*sizeof(bool)); + sim_size += 1; + sim[k] = x; + sim_prngs[k] = 0; + sim_isdirs[k] = true; + break; + } + } + // mark any related sim files as zombied for (lfs_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } - - // make the directory - char name[256]; - sprintf(name, "batman%03x", x); - lfsr_mkdir(&lfs, name) => 0; } // step the traversal @@ -9209,6 +9372,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -9233,6 +9400,10 @@ code = ''' assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS_TYPE_DIR); + assert(info.size == 0); + } else if (sim_isstickys[j]) { + assert(info.type == LFS_TYPE_STICKYNOTE); + assert(info.size == 0); } else { assert(info.type == LFS_TYPE_REG); assert(info.size == SIZE); @@ -9246,7 +9417,8 @@ code = ''' char name[256]; sprintf(name, "batman%03x", sim[j]); lfsr_file_t file; - lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) => LFS_ERR_ISDIR; + lfsr_file_open(&lfs, &file, name, LFS_O_RDONLY) + => LFS_ERR_ISDIR; } else { char name[256]; @@ -9261,8 +9433,12 @@ code = ''' } uint8_t rbuf[SIZE]; - lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, wbuf, SIZE) == 0); + if (sim_isstickys[j]) { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => 0; + } else { + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, wbuf, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; } } @@ -9284,6 +9460,8 @@ code = ''' // clean up sim/lfs free(sim); free(sim_prngs); + free(sim_isstickys); + free(sim_isdirs); for (lfs_size_t j = 0; j < sim_file_count; j++) { lfsr_file_close(&lfs, &sim_files[j]->file) => 0; free(sim_files[j]);