diff --git a/lfs.c b/lfs.c index fc887829..ec94fdc7 100644 --- a/lfs.c +++ b/lfs.c @@ -5118,20 +5118,6 @@ static int lfsr_mdir_lookupwide(lfs_t *lfs, const lfsr_mdir_t *mdir, } // track opened mdirs to keep state in-sync -static void lfsr_addopened(lfs_t *lfs, lfsr_opened_t *opened) { - opened->next = lfs->opened; - lfs->opened = opened; -} - -static void lfsr_removeopened(lfs_t *lfs, lfsr_opened_t *opened) { - for (lfsr_opened_t **p = &lfs->opened; *p; p = &(*p)->next) { - if (*p == opened) { - *p = (*p)->next; - break; - } - } -} - static bool lfsr_isopened(lfs_t *lfs, const lfsr_opened_t *opened) { for (lfsr_opened_t *p = lfs->opened; p; p = p->next) { if (p == opened) { @@ -5142,6 +5128,22 @@ static bool lfsr_isopened(lfs_t *lfs, const lfsr_opened_t *opened) { return false; } +static void lfsr_addopened(lfs_t *lfs, lfsr_opened_t *opened) { + LFS_ASSERT(!lfsr_isopened(lfs, opened)); + opened->next = lfs->opened; + lfs->opened = opened; +} + +static void lfsr_removeopened(lfs_t *lfs, lfsr_opened_t *opened) { + LFS_ASSERT(lfsr_isopened(lfs, opened)); + for (lfsr_opened_t **p = &lfs->opened; *p; p = &(*p)->next) { + if (*p == opened) { + *p = (*p)->next; + break; + } + } +} + /// Metadata-tree things /// @@ -10415,6 +10417,7 @@ static int lfsr_file_flush_(lfs_t *lfs, lfsr_file_t *file, lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file, void *buffer, lfs_size_t size) { + // can't read from writeonly files LFS_ASSERT(lfsr_o_isreadable(file->flags)); LFS_ASSERT(file->pos + size <= 0x7fffffff); @@ -10505,6 +10508,7 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file, lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, const void *buffer, lfs_size_t size) { + // can't write to readonly files LFS_ASSERT(lfsr_o_iswriteable(file->flags)); // would this write make our file larger than our size limit? @@ -10655,14 +10659,11 @@ failed:; } int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) { - // do nothing if our file is readonly - if (!lfsr_o_iswriteable(file->flags)) { - LFS_ASSERT(!lfsr_f_isunflushed(file->flags) - || (lfsr_file_size_(file) <= lfs->cfg->cache_size - && lfsr_file_size_(file) <= lfs->cfg->inline_size - && lfsr_file_size_(file) <= lfs->cfg->fragment_size)); - return 0; - } + // flushing readonly files is not supported + // + // in theory we could make this a noop, but that would be inconsistent + // with lfsr_file_sync + LFS_ASSERT(lfsr_o_iswriteable(file->flags)); // do nothing if our file is already flushed if (!lfsr_f_isunflushed(file->flags)) { @@ -10706,19 +10707,17 @@ failed:; } int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { + // syncing readonly files is not supported + // + // in theory we could make this a noop, but then syncing desynced + // readonly files would require disk writes + LFS_ASSERT(lfsr_o_iswriteable(file->flags)); + // do nothing if our file has been removed if (file->mdir.mid == -1) { return 0; } - // do nothing if our file is readonly - if (!lfsr_o_iswriteable(file->flags)) { - LFS_ASSERT(!lfsr_f_isunsynced(file->flags)); - // but do clear desync flag - file->flags &= ~LFS_O_DESYNC; - return 0; - } - // first flush any data in our buffer, this is a noop if already // flushed // @@ -10750,14 +10749,14 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { && file->buffer_size <= lfs->cfg->inline_size && file->buffer_size <= lfs->cfg->fragment_size)); - // checkpoint the allocator again - lfs_alloc_ckpoint(lfs); - - // don't write to disk if disk is already in-sync + // don't write to disk if our disk is already in-sync if (lfsr_f_isunsynced(file->flags)) { + // checkpoint the allocator again + lfs_alloc_ckpoint(lfs); + // commit our file's metadata uint8_t buf[LFSR_BTREE_DSIZE]; - int err = lfsr_mdir_commit(lfs, &file->mdir, LFSR_ATTRS( + err = lfsr_mdir_commit(lfs, &file->mdir, LFSR_ATTRS( (lfsr_f_isunflushed(file->flags) && file->buffer_size == 0) ? LFSR_ATTR(file->mdir.mid, WIDE(RM(STRUCT)), 0, @@ -10774,7 +10773,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { WIDE(BTREE), 0, FROMBTREE(&file->ftree.u.btree, buf)))); if (err) { - return err; + goto failed; } } @@ -10786,20 +10785,25 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { if (file_->type == LFS_TYPE_REG && file_->mdir.mid == file->mdir.mid // don't double update - && file_ != file - // don't update desynced file handles - && !lfsr_o_isdesync(file_->flags)) { - file_->flags &= ~LFS_F_UNSYNCED; - if (lfsr_f_isunflushed(file->flags)) { - file_->flags |= LFS_F_UNFLUSHED; + && file_ != file) { + // mark desynced files an unsynced + if (lfsr_o_isdesync(file_->flags)) { + file_->flags |= LFS_F_UNSYNCED; + + // update synced files } else { - file_->flags &= ~LFS_F_UNFLUSHED; + file_->flags &= ~LFS_F_UNSYNCED; + if (lfsr_f_isunflushed(file->flags)) { + file_->flags |= LFS_F_UNFLUSHED; + } else { + file_->flags &= ~LFS_F_UNFLUSHED; + } + file_->ftree = file->ftree; + file_->buffer_pos = file->buffer_pos; + LFS_ASSERT(file->buffer_size <= lfs->cfg->cache_size); + memcpy(file_->buffer, file->buffer, file->buffer_size); + file_->buffer_size = file->buffer_size; } - file_->ftree = file->ftree; - file_->buffer_pos = file->buffer_pos; - LFS_ASSERT(file->buffer_size <= lfs->cfg->cache_size); - memcpy(file_->buffer, file->buffer, file->buffer_size); - file_->buffer_size = file->buffer_size; } } diff --git a/tests/test_fsync.toml b/tests/test_fsync.toml index fcc6d55c..b6e583b1 100644 --- a/tests/test_fsync.toml +++ b/tests/test_fsync.toml @@ -2,6 +2,483 @@ after = 'test_fwrite' +# Some specific tests +[cases.test_fsync_sync_wrr] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - reader kept open, recvs updates from a + // c - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0; + + // write to a + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + + // should not show up in b yet + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => 0; + // or on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0; + lfsr_file_close(&lfs, &c) => 0; + + // sync a + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in b + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // lets rewrite a + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + + // b should still have previous contents + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // sync a + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // lets rewrite a one last time + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + + // b should still have previous contents + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // close a + lfsr_file_close(&lfs, &a) => 0; + + // now our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); + assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); + assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + lfsr_file_close(&lfs, &b) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_sync_wwrr] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a/b + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and b + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + + // should not show up in c yet + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0; + // or on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0; + lfsr_file_close(&lfs, &d) => 0; + + // sync a + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // b's contents were clobbered, so we should see a + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // now our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite a one last time + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // close a and b + lfsr_file_close(&lfs, &a) => 0; + lfsr_file_close(&lfs, &b) => 0; + + // now our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_sync_wwrr_noop] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a/b + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and b + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + + // should not show up in c yet + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0; + // or on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0; + lfsr_file_close(&lfs, &d) => 0; + + // sync a + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // b's contents were clobbered, so we should see a + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync a, without doing anything + lfsr_file_sync(&lfs, &a) => 0; + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b, this gets tricky + lfsr_file_sync(&lfs, &b) => 0; + + // b's contents were clobbered, so we should see a + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite b one last time, this time with sync + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + lfsr_file_sync(&lfs, &b) => 0; + + // now our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // close a and b + lfsr_file_close(&lfs, &a) => 0; + lfsr_file_close(&lfs, &b) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_sync_wwrr_append] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a/b + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and b + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + + // should not show up in c yet + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0; + // or on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => 0; + lfsr_file_close(&lfs, &d) => 0; + + // sync a + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // b's contents were clobbered, so we should see a + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // now our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // lets rewrite a one last time + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // close a and b + lfsr_file_close(&lfs, &a) => 0; + lfsr_file_close(&lfs, &b) => 0; + + // now our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!zdrasti!"); + assert(memcmp(rbuf, "hello!ohayo!zdrasti!", + strlen("hello!ohayo!zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!zdrasti!"); + assert(memcmp(rbuf, "hello!ohayo!zdrasti!", + strlen("hello!ohayo!zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + + # Test multiple readers, this shouldn't really have any issues [cases.test_fsync_rrrr] defines.R = 4 @@ -1221,6 +1698,519 @@ code = ''' # Desynced files make things interesting +# Some specific tests +[cases.test_fsync_desync_wdrr] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - reader kept open, recvs updates from a + // c - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0; + + // write to a and sync + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in b + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // now mark as desync, rewrite, and close + lfsr_file_desync(&lfs, &a) => 0; + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + lfsr_file_close(&lfs, &a) => 0; + + // b should still have previous contents + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // reopen a, mark as desync, rewrite a, and sync + lfsr_file_open(&lfs, &a, "jello", LFS_O_WRONLY) => 0; + lfsr_file_desync(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + // this sync should clear the desync + lfsr_file_sync(&lfs, &a) => 0; + + // now our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + // rewrite a, close, desync flag should have been cleared + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + lfsr_file_close(&lfs, &a) => 0; + + // our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &c) => 0; + + lfsr_file_close(&lfs, &b) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_desync_wrrd] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - reader kept open, recvs updates from a + // c - desynced reader + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_RDONLY) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; + + // write to a and sync + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in b + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // but not in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => 0; + // reopen c, should now be up to date + lfsr_file_close(&lfs, &c) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + + // rewrite and sync + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + // but not in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // reopen c, should now be up to date + lfsr_file_close(&lfs, &c) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + + // rewrite and close + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + lfsr_file_close(&lfs, &a) => 0; + + // our write should show up in b + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_read(&lfs, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); + assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); + // but not in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + // reopen c, should now be up to date + lfsr_file_close(&lfs, &c) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); + assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); + + lfsr_file_close(&lfs, &b) => 0; + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_desync_wdwdrr] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and sync + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // mark b as desync, rewrite, and close + lfsr_file_desync(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + lfsr_file_close(&lfs, &b) => 0; + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // reopen b, mark as desync, rewrite + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0; + lfsr_file_desync(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + // rewrite a, sync + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + lfsr_file_sync(&lfs, &a) => 0; + + // a should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // b should show up in c, without a's changes + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // rewrite b, close, desync flag should have been cleared + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!")) + => strlen("annyeong!"); + lfsr_file_close(&lfs, &b) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); + assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); + assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // rewrite a, close + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!")) + => strlen("czesc!"); + lfsr_file_close(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); + assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); + assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_desync_wdwdrr_noop] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and sync + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // mark b as desync, this should freeze its contents + lfsr_file_desync(&lfs, &b) => 0; + // rewrite a, sync + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + lfsr_file_sync(&lfs, &a) => 0; + + // a should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); + assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b, this may be tricky since we haven't touched b + lfsr_file_sync(&lfs, &b) => 0; + + // b should show up in c, without a's changes + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // rewrite b, close, desync flag should have been cleared + lfsr_file_rewind(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + lfsr_file_close(&lfs, &b) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); + assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // rewrite a, close + lfsr_file_rewind(&lfs, &a) => 0; + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + lfsr_file_close(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); + assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fsync_desync_wdwdrr_append] +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // a - writer + // b - writer + // c - reader kept open, recvs updates from a + // d - reader kept closed, checks disk state + lfsr_file_t a; + lfsr_file_t b; + lfsr_file_t c; + lfsr_file_t d; + uint8_t rbuf[256]; + lfsr_file_open(&lfs, &a, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL | LFS_O_APPEND) => 0; + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0; + lfsr_file_open(&lfs, &c, "jello", LFS_O_RDONLY) => 0; + + // write to a and sync + lfsr_file_write(&lfs, &a, "hello!", strlen("hello!")) + => strlen("hello!"); + lfsr_file_sync(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // mark b as desync, write, and close + lfsr_file_desync(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "bonjour!", strlen("bonjour!")) + => strlen("bonjour!"); + lfsr_file_close(&lfs, &b) => 0; + + // c should still have previous contents + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); + assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // reopen b, mark as desync, write + lfsr_file_open(&lfs, &b, "jello", LFS_O_WRONLY | LFS_O_APPEND) => 0; + lfsr_file_desync(&lfs, &b) => 0; + lfsr_file_write(&lfs, &b, "ohayo!", strlen("ohayo!")) + => strlen("ohayo!"); + // write a, sync + lfsr_file_write(&lfs, &a, "zdrasti!", strlen("zdrasti!")) + => strlen("zdrasti!"); + lfsr_file_sync(&lfs, &a) => 0; + + // a should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!"); + assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!"); + assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // sync b + lfsr_file_sync(&lfs, &b) => 0; + + // b should show up in c, without a's changes + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); + assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // write b, close, desync flag should have been cleared + lfsr_file_write(&lfs, &b, "annyeong!", strlen("annyeong!")) + => strlen("annyeong!"); + lfsr_file_close(&lfs, &b) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!annyeong!"); + assert(memcmp(rbuf, "hello!ohayo!annyeong!", + strlen("hello!ohayo!annyeong!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!annyeong!"); + assert(memcmp(rbuf, "hello!ohayo!annyeong!", + strlen("hello!ohayo!annyeong!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + // write a, close + lfsr_file_write(&lfs, &a, "czesc!", strlen("czesc!")) + => strlen("czesc!"); + lfsr_file_close(&lfs, &a) => 0; + + // our write should show up in c + lfsr_file_rewind(&lfs, &c) => 0; + lfsr_file_read(&lfs, &c, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!annyeong!czesc!"); + assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!", + strlen("hello!ohayo!annyeong!czesc!")) == 0); + // and on disk + lfsr_file_open(&lfs, &d, "jello", LFS_O_RDONLY) => 0; + lfsr_file_read(&lfs, &d, rbuf, sizeof(rbuf)) + => strlen("hello!ohayo!annyeong!czesc!"); + assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!", + strlen("hello!ohayo!annyeong!czesc!")) == 0); + lfsr_file_close(&lfs, &d) => 0; + + lfsr_file_close(&lfs, &c) => 0; + lfsr_unmount(&lfs) => 0; +''' + + # Test one desynced writer, multiple readers [cases.test_fsync_drrr] defines.R = 4