# Test multiple open file handles in different r/w configurations after = 'test_fwrite' # Some specific tests [cases.test_fsync_sync_wrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - reader kept open, recvs updates from a // c - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // should not show up in b yet lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => 0; // or on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &c) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // lets rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // now our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &c) => 0; lfs3_file_close(&lfs3, &b) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected [cases.test_fsync_sync_o_wsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - writer // b - reader kept open, recvs updates from a // c - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // should immediately show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // close a lfs3_file_close(&lfs3, &a) => 0; // should immediately show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &c) => 0; lfs3_file_close(&lfs3, &b) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_sync_wwrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - writer // c - reader kept open, recvs updates from a/b // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and b lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should not show up in c yet lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // or on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b's contents were clobbered, so we should see a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected [cases.test_fsync_sync_o_wswsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - synced writer // b - synced writer // c - reader kept open, recvs updates from a/b // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // should immediately show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // write to b lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // a's contents were clobbered, so we should see b lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // a's contents were clobbered, so we should see b lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // should still have a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test one non-LFS3_O_SYNC and one LFS3_O_SYNC [cases.test_fsync_sync_o_wwsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - writer // b - synced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // write to b lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen a with LFS3_O_TRUNC lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_TRUNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen LFS3_O_WRONLY and rewrite a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a and b one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!")) => strlen("moshimoshi!"); lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!")) => strlen("czesc!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // should still have b lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_sync_wwrr_zero] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - writer // c - reader kept open, recvs updates from a/b // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and b lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should not show up in c yet lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // or on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // truncate b, this gets tricky as we no longer have a struct lfs3_file_truncate(&lfs3, &b, 0) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // now our truncate should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // lets rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // truncate a one last time lfs3_file_truncate(&lfs3, &a, 0) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // now our truncate should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_sync_wwrr_noop] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - writer // c - reader kept open, recvs updates from a/b // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and b lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should not show up in c yet lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // or on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b's contents were clobbered, so we should see a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a, this may be tricky since we haven't touched b lfs3_file_sync(&lfs3, &a) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b, this may be tricky since we haven't touched b lfs3_file_sync(&lfs3, &b) => 0; // b's contents were clobbered, so we should see a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite b one last time, this time with sync lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_sync(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_sync_wwrr_append] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - writer // c - reader kept open, recvs updates from a/b // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and b lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should not show up in c yet lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // or on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b's contents were clobbered, so we should see a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // now our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple readers, this shouldn't really have any issues [cases.test_fsync_rrrr] defines.R = 4 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // read R handles in parallel lfs3_file_t readers[R]; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; assert(memcmp(rbuf, &before[i], CHUNK) == 0); } } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_rrrr_fuzz] defines.R = 4 defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = SEED; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // read R handles in parallel lfs3_file_t readers[R]; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < N; i++) { for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; assert(memcmp(rbuf, &before[off], size) == 0); } } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } lfs3_unmount(&lfs3) => 0; ''' # Test one writer, multiple readers [cases.test_fsync_wrrr] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &before[i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_wrrr_fuzz] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < N; i++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &before[off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple writers [cases.test_fsync_wwww] defines.W = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write W handles in parallel lfs3_file_t writers[W]; for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_open(&lfs3, &writers[w], "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t w = 0; w < W; w++) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writers[w], wbuf, CHUNK) => CHUNK; if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writers[w]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writers[w]) => 0; } memcpy(&after[i], wbuf, CHUNK); } } } for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_close(&lfs3, &writers[w]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_wwww_fuzz] defines.W = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write W files in parallel lfs3_file_t writers[W]; for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_open(&lfs3, &writers[w], "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t w = 0; w < W; w++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writers[w], off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writers[w], wbuf, size) => size; if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writers[w]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writers[w]) => 0; } memcpy(&after[off], wbuf, size); } } } for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_close(&lfs3, &writers[w]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple writers and multiple readers [cases.test_fsync_wwrr] defines.W = 4 defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write W handles, R handles in parallel lfs3_file_t writers[W]; lfs3_file_t readers[R]; for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_open(&lfs3, &writers[w], "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t w = 0; w < W; w++) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writers[w], wbuf, CHUNK) => CHUNK; if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writers[w]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writers[w]) => 0; } memcpy(&after[i], wbuf, CHUNK); } } for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &before[i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_close(&lfs3, &writers[w]) => 0; } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_wwrr_fuzz] defines.W = 4 defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write W files in parallel lfs3_file_t writers[W]; lfs3_file_t readers[R]; for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_open(&lfs3, &writers[w], "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t w = 0; w < W; w++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writers[w], off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writers[w], wbuf, size) => size; if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writers[w]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writers[w]) => 0; } memcpy(&after[off], wbuf, size); } } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &before[off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } for (lfs3_size_t w = 0; w < W; w++) { lfs3_file_close(&lfs3, &writers[w]) => 0; } for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rd/wrers [cases.test_fsync_rwrw] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t between[RW][SIZE]; for (lfs3_size_t rw = 0; rw < RW; rw++) { memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, CHUNK) => CHUNK; memcpy(&between[rw][i], wbuf, CHUNK); if (SYNC == 0) { if (rw == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } memcpy(&after[i], wbuf, CHUNK); } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_rwrw_fuzz] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t between[RW][SIZE]; for (lfs3_size_t rw = 0; rw < RW; rw++) { memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; memcpy(&between[rw][off], wbuf, size); if (SYNC == 0) { if (rw == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } memcpy(&after[off], wbuf, size); } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &between[rw][off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rw files without fixed size [cases.test_fsync_rwrw_sparse_fuzz] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 2; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); } // broadcast sim? if (SYNC) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rw files while also truncating/fruncating [cases.test_fsync_rwtfrwtf_sparse_fuzz] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [0, 1, 2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 1, 2, 3] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 4; bool broadcast = false; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); if (SYNC) { broadcast = true; } // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); // truncating? } else if (op == 2) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // truncate lfs3_file_truncate(&lfs3, &rdwrs[rw], size) => 0; if (FLUSH == 1) { // (flush does nothing) lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } // update the sim if (size > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, size - between_size[rw]); } between_size[rw] = size; // note LFS3_O_SYNC does _not_ sync truncates if (SYNC == 1) { broadcast = true; } // fruncating? } else if (op == 3) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // fruncate lfs3_file_fruncate(&lfs3, &rdwrs[rw], size) => 0; if (FLUSH == 1) { // (flush does nothing) lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } // update the sim if (size > between_size[rw]) { memmove(&between[rw][size - between_size[rw]], between[rw], between_size[rw]); memset(between[rw], 0, size - between_size[rw]); } else { memmove(between[rw], &between[rw][between_size[rw] - size], size); } between_size[rw] = size; // note LFS3_O_SYNC does _not_ sync fruncates if (SYNC == 1) { broadcast = true; } } // broadcast sim? if (broadcast) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Desynced files make things interesting # Some specific tests [cases.test_fsync_desync_wdrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - desynced writer // b - reader kept open, recvs updates from a // c - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // now mark as desync, rewrite, and close lfs3_file_desync(&lfs3, &a) => 0; lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_close(&lfs3, &a) => 0; // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // reopen a, mark as desync, rewrite a, and sync lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY) => 0; lfs3_file_desync(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // this sync should clear the desync lfs3_file_sync(&lfs3, &a) => 0; // now our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // rewrite a, close, desync flag should have been cleared lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &c) => 0; lfs3_file_close(&lfs3, &b) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_desync_wrrd] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - reader kept open, recvs updates from a // c - desynced reader lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // but not in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // reopen c, should now be up to date lfs3_file_close(&lfs3, &c) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // rewrite and sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // but not in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // reopen c, should now be up to date lfs3_file_close(&lfs3, &c) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // rewrite and close lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // but not in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // reopen c, should now be up to date lfs3_file_close(&lfs3, &c) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &b) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_desync_wwdrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, rewrite, and close lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_close(&lfs3, &b) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen b, mark as desync, rewrite lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b should show up in c, without a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b, close, desync flag should have been cleared lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); lfs3_file_close(&lfs3, &b) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a, close lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!")) => strlen("czesc!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected [cases.test_fsync_desync_o_wsdwsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - desynced writer // b - writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0) | LFS3_O_DESYNC) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to b lfs3_file_write(&lfs3, &b, "hello!", strlen("hello!")) => strlen("hello!"); // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // should still have a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0) | LFS3_O_DESYNC) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // desync a lfs3_file_desync(&lfs3, &a) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!")) => strlen("czesc!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // should still have a lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test one non-LFS3_O_SYNC and one LFS3_O_SYNC [cases.test_fsync_desync_o_wdwsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - desynced writer // b - synced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // write to b lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen a with LFS3_O_TRUNC lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_TRUNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; // c should still have a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // reopen LFS3_O_WRONLY and rewrite a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // lets rewrite a and b one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!")) => strlen("moshimoshi!"); lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!")) => strlen("czesc!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // should still have b lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!i!"); assert(memcmp(rbuf, "czesc!i!", strlen("czesc!i!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_desync_wwdrr_zero] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, truncate, and close, this gets tricky as we no // longer have a struct lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_truncate(&lfs3, &b, 0) => 0; lfs3_file_close(&lfs3, &b) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen b, mark as desync, truncate lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_truncate(&lfs3, &b, 0) => 0; // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b should show up in c, without a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // rewrite b, close, desync flag should have been cleared lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); lfs3_file_close(&lfs3, &b) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // truncate a, close lfs3_file_truncate(&lfs3, &a, 0) => 0; lfs3_file_close(&lfs3, &a) => 0; // our truncate should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_desync_wwdrr_noop] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, this should freeze its contents lfs3_file_desync(&lfs3, &b) => 0; // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b, this may be tricky since we haven't touched b lfs3_file_sync(&lfs3, &b) => 0; // b should show up in c, without a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b, close, desync flag should have been cleared lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_close(&lfs3, &b) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a, close lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_desync_wwdrr_append] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, write, and close lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_close(&lfs3, &b) => 0; // c should still have previous contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen b, mark as desync, write lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0; lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // write a, sync lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!"); assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!zdrasti!"); assert(memcmp(rbuf, "hello!zdrasti!", strlen("hello!zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // b should show up in c, without a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // write b, close, desync flag should have been cleared lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); lfs3_file_close(&lfs3, &b) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!annyeong!"); assert(memcmp(rbuf, "hello!ohayo!annyeong!", strlen("hello!ohayo!annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!annyeong!"); assert(memcmp(rbuf, "hello!ohayo!annyeong!", strlen("hello!ohayo!annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // write a, close lfs3_file_write(&lfs3, &a, "czesc!", strlen("czesc!")) => strlen("czesc!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &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 lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!annyeong!czesc!"); assert(memcmp(rbuf, "hello!ohayo!annyeong!czesc!", strlen("hello!ohayo!annyeong!czesc!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test one desynced writer, multiple readers [cases.test_fsync_drrr] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | LFS3_O_DESYNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &before[i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_drrr_fuzz] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | LFS3_O_DESYNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < N; i++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &before[off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test one writer, multiple desynced readers [cases.test_fsync_wddd] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; assert(memcmp(rbuf, &before[i], CHUNK) == 0); } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_wddd_fuzz] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < N; i++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; assert(memcmp(rbuf, &before[off], size) == 0); } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple desynced rd/wrers [cases.test_fsync_rwdrwd] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t between[RW][SIZE]; for (lfs3_size_t rw = 0; rw < RW; rw++) { memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_DESYNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, CHUNK) => CHUNK; memcpy(&between[rw][i], wbuf, CHUNK); if (SYNC == 0) { if (rw == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } if (i == 0) { memcpy(after, between[rw], SIZE); } else { memcpy(&after[i], wbuf, CHUNK); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_seek(&lfs3, &rdwrs[rw], i, LFS3_SEEK_SET) => i; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_rwdrwd_fuzz] defines.RW = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t between[RW][SIZE]; for (lfs3_size_t rw = 0; rw < RW; rw++) { memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_DESYNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; memcpy(&between[rw][off], wbuf, size); if (SYNC == 0) { if (rw == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; } if (i == 0) { memcpy(after, between[rw], SIZE); } else { memcpy(&after[off], wbuf, size); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &between[rw][off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rwd files without fixed size [cases.test_fsync_rwdrwd_sparse_fuzz] defines.RW = 4 # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; bool between_desync[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 2; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 3; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); } // desync? if (sync == 0) { lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // broadcast sim memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // otherwise no change } else if (sync == 2) { // do nothing } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rwd files while also truncating/fruncating [cases.test_fsync_rwtfdrwtfd_sparse_fuzz] defines.RW = 4 # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; bool between_desync[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 4; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 3; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); // truncating? } else if (op == 2) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // truncate lfs3_file_truncate(&lfs3, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, size - between_size[rw]); } between_size[rw] = size; // fruncating? } else if (op == 3) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // fruncate lfs3_file_fruncate(&lfs3, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memmove(&between[rw][size - between_size[rw]], between[rw], between_size[rw]); memset(between[rw], 0, size - between_size[rw]); } else { memmove(between[rw], &between[rw][between_size[rw] - size], size); } between_size[rw] = size; } // desync? if (sync == 0) { lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // broadcast sim memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // resync? } else if (sync == 2) { lfs3_file_resync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // update sim memcpy(between[rw], after, SIZE); between_size[rw] = after_size; // otherwise no change } else if (sync == 2) { // do nothing } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Resyncing makes things even more interesting # Some specific tests [cases.test_fsync_resync_rwyrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - reader kept open, recvs updates from a // c - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // our write should show up in a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // resync a lfs3_file_resync(&lfs3, &a) => 0; // a should have reverted to previous contents lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // our write should show up in a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_read(&lfs3, &a, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // b should still have previous contents lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &c) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // now our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &c) => 0; lfs3_file_close(&lfs3, &b) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_resync_wrrdy] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - reader kept open, recvs updates from a // c - desynced reader lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_RDONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // but not in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // resync c, should now be up to date lfs3_file_resync(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // rewrite and sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and in c now that we resynced lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // desync c, rewrite a and sync lfs3_file_desync(&lfs3, &c) => 0; lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // but not in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // resync c, should now be up to date lfs3_file_resync(&lfs3, &c) => 0; lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // desync c, rewrite a and close lfs3_file_desync(&lfs3, &c) => 0; lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_close(&lfs3, &a) => 0; // our write should show up in b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_read(&lfs3, &b, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // but not in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // resync c, should now be up to date lfs3_file_resync(&lfs3, &c) => 0; lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &b) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_resync_wwdyrr] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, rewrite lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // c should still show a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // desync b lfs3_file_desync(&lfs3, &b) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // rewrite b, close lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_close(&lfs3, &b) => 0; // b should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that LFS3_O_SYNC/LFS3_O_FLUSH flags work as expected [cases.test_fsync_resync_o_wsdywsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2, 3] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - desynced synced writer // b - synced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0) | LFS3_O_DESYNC) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to b lfs3_file_write(&lfs3, &b, "hello!", strlen("hello!")) => strlen("hello!"); // should immediately show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // reopen a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0) | LFS3_O_DESYNC) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // reopen a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0) | LFS3_O_DESYNC) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync a lfs3_file_resync(&lfs3, &a) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // desync a lfs3_file_desync(&lfs3, &a) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("zdrasti!"); assert(memcmp(rbuf, "zdrasti!", strlen("zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should revert to a's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // desync b lfs3_file_desync(&lfs3, &b) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!")) => strlen("czesc!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close b lfs3_file_close(&lfs3, &b) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # test one non-LFS3_O_SYNC and one LFS3_O_SYNC [cases.test_fsync_resync_o_wdywsrr] # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC # SYNC=3 => sync via LFS3_M_SYNC defines.SYNC = [2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH # FLUSH=3 => flush via LFS3_M_FLUSH defines.FLUSH = [0, 2, 3] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR | ((FLUSH == 3) ? LFS3_M_FLUSH : 0) | ((SYNC == 3) ? LFS3_M_SYNC : 0), CFG) => 0; // a - desynced writer // b - synced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); // write to b lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // should immediately show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync a lfs3_file_resync(&lfs3, &a) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen a with LFS3_O_TRUNC lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_TRUNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("bonjour!"); assert(memcmp(rbuf, "bonjour!", strlen("bonjour!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync a lfs3_file_resync(&lfs3, &a) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // reopen LFS3_O_WRONLY and rewrite a lfs3_file_close(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &a, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!r!"); assert(memcmp(rbuf, "ohayo!r!", strlen("ohayo!r!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite b lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync a lfs3_file_resync(&lfs3, &a) => 0; // sync a lfs3_file_sync(&lfs3, &a) => 0; // c should still have b's contents lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("annyeong!"); assert(memcmp(rbuf, "annyeong!", strlen("annyeong!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // desync a lfs3_file_desync(&lfs3, &a) => 0; // lets rewrite a and b one last time lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "moshimoshi!", strlen("moshimoshi!")) => strlen("moshimoshi!"); lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "czesc!", strlen("czesc!")) => strlen("czesc!"); // resync a lfs3_file_resync(&lfs3, &a) => 0; // should immediately show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a and b lfs3_file_close(&lfs3, &a) => 0; lfs3_file_close(&lfs3, &b) => 0; // should still have b lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("czesc!ng!"); assert(memcmp(rbuf, "czesc!ng!", strlen("czesc!ng!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_resync_wwdyrr_zero] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, rewrite lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // truncate a, sync, this gets tricky as we no longer have a struct lfs3_file_truncate(&lfs3, &a, 0) => 0; lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // c should still show a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // desync b lfs3_file_desync(&lfs3, &b) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // truncate b, close lfs3_file_truncate(&lfs3, &b, 0) => 0; lfs3_file_close(&lfs3, &b) => 0; // b should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => 0; // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => 0; lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_resync_wwdyrr_noop] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, rewrite lfs3_file_desync(&lfs3, &b) => 0; // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync b, this may be tricky since we haven't touched b lfs3_file_resync(&lfs3, &b) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // c should still show a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // desync b lfs3_file_desync(&lfs3, &b) => 0; // resync b, close, this may be tricky since we haven't touched b lfs3_file_resync(&lfs3, &b) => 0; lfs3_file_close(&lfs3, &b) => 0; // b should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("ohayo!"); assert(memcmp(rbuf, "ohayo!", strlen("ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_resync_wwdyrr_append] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // a - writer // b - desynced writer // c - reader kept open, recvs updates from a // d - reader kept closed, checks disk state lfs3_file_t a; lfs3_file_t b; lfs3_file_t c; lfs3_file_t d; uint8_t rbuf[256]; lfs3_file_open(&lfs3, &a, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_APPEND) => 0; lfs3_file_sync(&lfs3, &a) => 0; lfs3_file_open(&lfs3, &b, "jello", LFS3_O_WRONLY | LFS3_O_APPEND) => 0; lfs3_file_open(&lfs3, &c, "jello", LFS3_O_RDONLY) => 0; // write to a and sync lfs3_file_write(&lfs3, &a, "hello!", strlen("hello!")) => strlen("hello!"); lfs3_file_sync(&lfs3, &a) => 0; // our write should show up in c lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!"); assert(memcmp(rbuf, "hello!", strlen("hello!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // mark b as desync, rewrite lfs3_file_desync(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "bonjour!", strlen("bonjour!")) => strlen("bonjour!"); // rewrite a, sync lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "ohayo!", strlen("ohayo!")) => strlen("ohayo!"); lfs3_file_sync(&lfs3, &a) => 0; // a should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // sync b lfs3_file_sync(&lfs3, &b) => 0; // c should still show a's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!"); assert(memcmp(rbuf, "hello!ohayo!", strlen("hello!ohayo!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // rewrite a lfs3_file_rewind(&lfs3, &a) => 0; lfs3_file_write(&lfs3, &a, "annyeong!", strlen("annyeong!")) => strlen("annyeong!"); // desync b lfs3_file_desync(&lfs3, &b) => 0; // resync b lfs3_file_resync(&lfs3, &b) => 0; // rewrite b, close lfs3_file_rewind(&lfs3, &b) => 0; lfs3_file_write(&lfs3, &b, "zdrasti!", strlen("zdrasti!")) => strlen("zdrasti!"); lfs3_file_close(&lfs3, &b) => 0; // b should show up in c lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; // close a lfs3_file_close(&lfs3, &a) => 0; // c should still show b's changes lfs3_file_rewind(&lfs3, &c) => 0; lfs3_file_read(&lfs3, &c, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); // and on disk lfs3_file_open(&lfs3, &d, "jello", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &d, rbuf, sizeof(rbuf)) => strlen("hello!ohayo!zdrasti!"); assert(memcmp(rbuf, "hello!ohayo!zdrasti!", strlen("hello!ohayo!zdrasti!")) == 0); lfs3_file_close(&lfs3, &d) => 0; lfs3_file_close(&lfs3, &c) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test one resyncing writer, multiple readers [cases.test_fsync_yrrr] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | LFS3_O_DESYNC | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK; // resync lfs3_file_resync(&lfs3, &writer) => 0; memcpy(&after[i], wbuf, CHUNK); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; if (SYNC == 0 || SYNC == 1) { assert(memcmp(rbuf, &before[i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0 || SYNC == 1) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_yrrr_fuzz] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY) => 0; } for (lfs3_size_t i = 0; i < N; i++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, size) => size; // resync lfs3_file_resync(&lfs3, &writer) => 0; memcpy(&after[off], wbuf, size); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; if (SYNC == 0 || SYNC == 1) { assert(memcmp(rbuf, &before[off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; if (SYNC == 0 || SYNC == 1) { assert(memcmp(rbuf, before, SIZE) == 0); } else { assert(memcmp(rbuf, after, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test one writer, multiple resyncing readers [cases.test_fsync_wyyy] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // resync desync readers lfs3_file_resync(&lfs3, &readers[r]) => 0; lfs3_file_desync(&lfs3, &readers[r]) => 0; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { assert(memcmp(rbuf, &before[i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_fsync_wyyy_fuzz] defines.R = 4 # SYNC=0 => no sync, readers not updated # SYNC=1 => sync via lfs3_file_sync # SYNC=2 => sync via LFS3_O_SYNC defines.SYNC = [0, 1, 2] # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 20 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file uint32_t prng = 42; uint8_t before[SIZE]; for (lfs3_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t after[SIZE]; memcpy(after, before, SIZE); lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, before, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; // write 1 handle, read R handles in parallel lfs3_file_t writer; lfs3_file_t readers[R]; lfs3_file_open(&lfs3, &writer, "jello", LFS3_O_WRONLY | ((FLUSH == 2) ? LFS3_O_FLUSH : 0) | ((SYNC == 2) ? LFS3_O_SYNC : 0)) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_open(&lfs3, &readers[r], "jello", LFS3_O_RDONLY | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < N; i++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &writer, off, LFS3_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); if (FLUSH == 1) { lfs3_file_flush(&lfs3, &writer) => 0; } if (SYNC == 1) { lfs3_file_sync(&lfs3, &writer) => 0; } for (lfs3_size_t r = 0; r < R; r++) { // choose a random offset lfs3_off_t off = TEST_PRNG(&prng) % SIZE; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); lfs3_file_seek(&lfs3, &readers[r], off, LFS3_SEEK_SET) => off; // resync desync readers lfs3_file_resync(&lfs3, &readers[r]) => 0; lfs3_file_desync(&lfs3, &readers[r]) => 0; // read uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &readers[r], rbuf, CHUNK) => size; if (SYNC == 0) { assert(memcmp(rbuf, &before[off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } lfs3_file_close(&lfs3, &writer) => 0; for (lfs3_size_t r = 0; r < R; r++) { lfs3_file_close(&lfs3, &readers[r]) => 0; } // check that file was written as expected lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, after, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rwd files without fixed size [cases.test_fsync_rwdyrwdy_sparse_fuzz] defines.RW = 4 # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; bool between_desync[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 2; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 4; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); } // desync? if (sync == 0) { lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // broadcast sim memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // resync? } else if (sync == 2) { lfs3_file_resync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // update sim memcpy(between[rw], after, SIZE); between_size[rw] = after_size; // otherwise no change } else if (sync == 3) { // do nothing } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # Test multiple rwd files while also truncating/fruncating [cases.test_fsync_rwtfdyrwtfdy_sparse_fuzz] defines.RW = 4 # FLUSH=0 => no flush # FLUSH=1 => flush via lfs3_file_flush # FLUSH=2 => flush via LFS3_O_FLUSH defines.FLUSH = [0, 1, 2] defines.N = 40 defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' defines.SEED = 'range(100)' fuzz = 'SEED' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs3_size_t between_size[RW]; bool between_desync[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs3_size_t after_size = 0; // write RW rdwrs in parallel lfs3_file_t rdwrs[RW]; for (lfs3_size_t rw = 0; rw < RW; rw++) { // open files lfs3_file_open(&lfs3, &rdwrs[rw], "jello", LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH == 2) ? LFS3_O_FLUSH : 0)) => 0; } for (lfs3_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs3_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 4; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 4; // writing? if (op == 0) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs3_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; lfs3_file_write(&lfs3, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfs3_file_flush(&lfs3, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs3_max(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs3_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs3_size_t size = lfs3_min(CHUNK, between_size[rw] - off); // read lfs3_file_seek(&lfs3, &rdwrs[rw], off, LFS3_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); // truncating? } else if (op == 2) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // truncate lfs3_file_truncate(&lfs3, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, size - between_size[rw]); } between_size[rw] = size; // fruncating? } else if (op == 3) { // choose a random new file size lfs3_off_t size = TEST_PRNG(&prng) % SIZE; // fruncate lfs3_file_fruncate(&lfs3, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memmove(&between[rw][size - between_size[rw]], between[rw], between_size[rw]); memset(between[rw], 0, size - between_size[rw]); } else { memmove(between[rw], &between[rw][between_size[rw] - size], size); } between_size[rw] = size; } // desync? if (sync == 0) { lfs3_file_desync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfs3_file_sync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // broadcast sim memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // resync? } else if (sync == 2) { lfs3_file_resync(&lfs3, &rdwrs[rw]) => 0; between_desync[rw] = false; // update sim memcpy(between[rw], after, SIZE); between_size[rw] = after_size; // otherwise no change } else if (sync == 3) { // do nothing } } } for (lfs3_size_t rw = 0; rw < RW; rw++) { // close files lfs3_file_close(&lfs3, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs3_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jello", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; '''