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]);