# Test orphaned files and their various use cases after = ['test_fwrite', 'test_fsync'] # test files don't exist until first sync/close [cases.test_stickynotes_uncreat] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // check that the file doesn't _really_ exist lfs3_t lfs3_; lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0; // via stat struct lfs3_info info; lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3_) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // check that the file still doesn't _really_ exist lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3_) => 0; } if (SYNC) { // sync the file lfs3_file_sync(&lfs3, &file) => 0; // now it should show up lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; } // close the file lfs3_file_close(&lfs3, &file) => 0; // now it should show up lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; lfs3_unmount(&lfs3) => 0; // should still be there lfs3_mount(&lfs3_, LFS3_M_RDONLY, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; ''' # test files don't exist until first sync/close, even under powerloss [cases.test_stickynotes_uncreat_pl] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' reentrant = true code = ''' // format once per test lfs3_t lfs3; int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG); if (err) { lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // create a file // // note the excl flag lfs3_file_t file; err = lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL); assert(!err || err == LFS3_ERR_EXIST); if (err != LFS3_ERR_EXIST) { // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // close the file lfs3_file_close(&lfs3, &file) => 0; } // we should be able to read the file now lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0; uint32_t prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; // and after remounting lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # test files don't exist until first sync/close, even under powerloss [cases.test_stickynotes_uncreat_many_pl] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', ] defines.CHUNK = 8 defines.N = 128 reentrant = true code = ''' // format once per test lfs3_t lfs3; int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG); if (err) { lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // create N files for (lfs3_size_t j = 0; j < N; j++) { // note the excl flag char name[256]; sprintf(name, "batman%03x", j); lfs3_file_t file; err = lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL); assert(!err || err == LFS3_ERR_EXIST); if (err != LFS3_ERR_EXIST) { // write to the file uint32_t prng = 42 + j; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // close the file lfs3_file_close(&lfs3, &file) => 0; } } // we should be able to read the files now for (lfs3_size_t j = 0; j < N; j++) { char name[256]; sprintf(name, "batman%03x", j); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t prng = 42 + j; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; } // and after remounting lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; for (lfs3_size_t j = 0; j < N; j++) { char name[256]; sprintf(name, "batman%03x", j); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t prng = 42 + j; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test files only exist as stickynotes until first sync/close [cases.test_stickynotes_uncreat_sync_wr] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // uncommitted files appear as stickynotes // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } // but we should still recieve sync broadcasts on sync/close lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } if (SYNC) { // sync the file lfs3_file_sync(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close the file lfs3_file_close(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file__) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_sync_rw] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // open a second reference lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // uncommitted files appear as stickynotes // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // write to the second file uint32_t prng = 42; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } if (SYNC) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close the second file lfs3_file_close(&lfs3, &file__) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_wdwr] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // open a second reference lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // and a third for checking sync broadcasts lfs3_file_t file___; lfs3_file_open(&lfs3, &file___, "batman", LFS3_O_RDWR | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // write to the first file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // uncommitted files appear as stickynotes // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // uncommitted files appear as stickynotes // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } if (SYNC) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close the second file lfs3_file_close(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // now sync the first file, this should overwrite what is written lfs3_file_sync(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // and close, this shouldn't change anything // // note we must sync to clear the desync flag lfs3_file_close(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file___) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_open] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the uncreat lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; // our uncreat should have been overwritten lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_excl] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to create a new file over the uncreat // // this errors, otherwise it's easy to create the same file twice // with excl, which would be super duper confusing for users // lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => LFS3_ERR_EXIST; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the excl file had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_mkdir] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to mkdir // // this should fail because of the stickynote // lfs3_mkdir(&lfs3, "batman") => LFS3_ERR_EXIST; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the excl file had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can remove stickynotes [cases.test_stickynotes_uncreat_rm] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // remove the stickynote lfs3_remove(&lfs3, "batman") => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // the stickynote should be gone // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can rename stickynotes [cases.test_stickynotes_uncreat_mv] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename the stickynote lfs3_rename(&lfs3, "batman", "catman") => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the stickynote survived the rename // via stat struct lfs3_info info; lfs3_stat(&lfs3, "catman", &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can rename stickynotes onto files [cases.test_stickynotes_uncreat_mv_replace] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreate lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // create another file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "catman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename the stickynote lfs3_rename(&lfs3, "batman", "catman") => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the stickynote survived the rename // via stat struct lfs3_info info; lfs3_stat(&lfs3, "catman", &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can rename stickynotes onto other files [cases.test_stickynotes_uncreat_mv_src_file] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreate lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename the stickynote lfs3_rename(&lfs3, "batman", "catman") => 0; // we should still be able to read our uncreate/file lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); lfs3_file_rewind(&lfs3, &file) => 0; lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &file) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the stickynote survived the rename // via stat struct lfs3_info info; lfs3_stat(&lfs3, "catman", &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can rename files onto stickynotes [cases.test_stickynotes_uncreat_mv_dst_file] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreate lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename onto the stickynote lfs3_rename(&lfs3, "catman", "batman") => 0; // we should still be able to read our uncreate/file lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); lfs3_file_rewind(&lfs3, &file) => 0; lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &file) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // test that the file replaced the stickynote // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test we can rename stickynotes onto other stickynotes [cases.test_stickynotes_uncreat_mv_stickynote] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreate lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // create another uncreate lfs3_file_t uncreat_; lfs3_file_open(&lfs3, &uncreat_, "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat_, "WoO~", strlen("WoO~")) => strlen("WoO~"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename the stickynote lfs3_rename(&lfs3, "batman", "catman") => 0; // we should still be able to read our uncreates lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); lfs3_file_rewind(&lfs3, &uncreat_) => 0; lfs3_file_read(&lfs3, &uncreat_, rbuf, sizeof(rbuf)) => strlen("WoO~"); assert(memcmp(rbuf, "WoO~", strlen("WoO~")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &uncreat_) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the stickynote survived the rename // via stat struct lfs3_info info; lfs3_stat(&lfs3, "catman", &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "catman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; lfs3_file_close(&lfs3, &uncreat_) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test renaming a stickynote onto itself does nothing [cases.test_stickynotes_uncreat_mv_noop] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreate lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename the stickynote lfs3_rename(&lfs3, "batman", "batman") => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the stickynote survived the rename // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_not_dir] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to rename a dir onto the stickynote // // this should fail because of the stickynote // lfs3_mkdir(&lfs3, "datman") => 0; lfs3_rename(&lfs3, "datman", "batman") => LFS3_ERR_NOTDIR; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the mkdir had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_stat(&lfs3, "datman", &info) => 0; assert(strcmp(info.name, "datman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "datman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_not_stickynote] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to rename the stickynote onto a dir // // this should fail because of the stickynote // lfs3_mkdir(&lfs3, "datman") => 0; lfs3_rename(&lfs3, "batman", "datman") => LFS3_ERR_ISDIR; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the mkdir had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_stat(&lfs3, "datman", &info) => 0; assert(strcmp(info.name, "datman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "datman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_not_root] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to rename the stickynote onto the root // // this should fail because of the stickynote // // well, because of the root really, but also because of the // stickynote // lfs3_rename(&lfs3, "batman", "/") => LFS3_ERR_INVAL; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the mkdir had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_uncreat_not_noent] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // attempt to rename the stickynote onto an invalid path // // this should fail because of the stickynote // // well, because of the invalid path really, but also because of the // stickynote // lfs3_rename(&lfs3, "batman", "no/batman") => LFS3_ERR_NOENT; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the mkdir had no effect // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (CLOSE == 1) { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("WoOoOoOoOoO")); } else { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; if (CLOSE == 1) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test desync files don't exist until first sync + close [cases.test_stickynotes_undesync] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // check that the file doesn't _really_ exist lfs3_t lfs3_; lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0; // via stat struct lfs3_info info; lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3_) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // check that the file still doesn't _really_ exist lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3_) => 0; } if (SYNC) { // sync the file lfs3_file_sync(&lfs3, &file) => 0; // now it should show up lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; } // sync the file lfs3_file_sync(&lfs3, &file) => 0; // close the file lfs3_file_close(&lfs3, &file) => 0; // now it should show up lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; lfs3_unmount(&lfs3) => 0; // should still be there lfs3_mount(&lfs3_, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3_, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3_, &dir, "/") => 0; lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3_, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3_, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3_, &dir) => 0; // via open lfs3_file_open(&lfs3_, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3_, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3_, &file_) => 0; lfs3_unmount(&lfs3_) => 0; ''' # test desync files don't exist until first sync + close, even under powerloss [cases.test_stickynotes_undesync_pl] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' reentrant = true code = ''' // format once per test lfs3_t lfs3; int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG); if (err) { lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // create a desync file // // note the excl flag lfs3_file_t file; err = lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC); assert(!err || err == LFS3_ERR_EXIST); if (err != LFS3_ERR_EXIST) { // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // sync the file lfs3_file_sync(&lfs3, &file) => 0; // close the file lfs3_file_close(&lfs3, &file) => 0; } // we should be able to read the file now lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0; uint32_t prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; // and after remounting lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # test desync files don't exist until first sync + close, even under powerloss [cases.test_stickynotes_undesync_many_pl] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', ] defines.CHUNK = 8 defines.N = 128 reentrant = true code = ''' // format once per test lfs3_t lfs3; int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG); if (err) { lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // create N desync files lfs3_file_t files[N]; bool exists[N]; for (lfs3_size_t j = 0; j < N; j++) { // note the excl flag char name[256]; sprintf(name, "batman%03x", j); err = lfs3_file_open(&lfs3, &files[j], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC); assert(!err || err == LFS3_ERR_EXIST); exists[j] = (err == LFS3_ERR_EXIST); if (!exists[j]) { // write to the file uint32_t prng = 42 + j; for (lfs3_off_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, &files[j], wbuf, CHUNK) => CHUNK; } } } // sync and close for (lfs3_size_t j = 0; j < N; j++) { if (!exists[j]) { // sync the file lfs3_file_sync(&lfs3, &files[j]) => 0; // close the file lfs3_file_close(&lfs3, &files[j]) => 0; } } // we should be able to read the files now for (lfs3_size_t j = 0; j < N; j++) { char name[256]; sprintf(name, "batman%03x", j); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t prng = 42 + j; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; } // and after remounting lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; for (lfs3_size_t j = 0; j < N; j++) { char name[256]; sprintf(name, "batman%03x", j); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t prng = 42 + j; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test desync files aren't even openable until first sync + close [cases.test_stickynotes_undesync_sync_wr] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, the file does not exist yet // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, the file does not exist yet // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; } // but we should still recieve sync broadcasts on sync + close lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } if (SYNC) { // sync the file lfs3_file_sync(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // sync the file lfs3_file_sync(&lfs3, &file) => 0; // close the file lfs3_file_close(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file__) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_undesync_sync_rw] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // open a second desync reference lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_DESYNC) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // uncommitted files appear as stickynotes // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // write to the second file uint32_t prng = 42; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } if (SYNC) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // close the second file lfs3_file_close(&lfs3, &file__) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_undesync_wdwr] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // open a second desync reference lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // and a third for checking sync broadcasts lfs3_file_t file___; lfs3_file_open(&lfs3, &file___, "batman", LFS3_O_RDWR | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // write to the first file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } if (SYNC) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // close the second file lfs3_file_close(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // now sync the first file, this should overwrite what is written lfs3_file_sync(&lfs3, &file) => 0; // now it should show up // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // and close, this shouldn't change anything // // note we must sync to clear the desync flag lfs3_file_close(&lfs3, &file) => 0; // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file___) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_undesync_open] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desynced uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the uncreat lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_undesync_excl] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op defines.CLOSE = [0, 1] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op defines.REMOUNT = [0, 1] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desynced uncreat lfs3_file_t uncreat; lfs3_file_open(&lfs3, &uncreat, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &uncreat, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the uncreat // // while it's still possible for the desynced uncreat to create the // file by explicitly calling lfs3_file_sync, for the most part we // treat desynced files like zombies and allow excl creates // // this makes lfs3_file_sync/resync roughly the same as opening the // file after the excl create succeeds, and if you're using desynced // files you should probably be aware of littlefs's snapshot model // anyways // lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; // we should still be able to read our uncreat lfs3_file_rewind(&lfs3, &uncreat) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &uncreat, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &uncreat) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &uncreat) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test that desynced, and then closed, files don't show up [cases.test_stickynotes_orphan] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, the file does not exist yet // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; } // close lfs3_file_close(&lfs3, &file) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // because the file was desynced, it should still not exist // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // even after a remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // because the file was desynced, it should still not exist // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_wdwr] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' # SYNC=0x1 => sync before orphaning # SYNC=0x2 => sync after orphaning defines.SYNC = [0, 1, 2, 3] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; // open a second reference lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // and a third for checking sync broadcasts lfs3_file_t file___; lfs3_file_open(&lfs3, &file___, "batman", LFS3_O_RDWR | LFS3_O_CREAT) => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // write to the first file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; } if (SYNC & 0x1) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close the first file, this should do nothing but discard the // file contents lfs3_file_close(&lfs3, &file) => 0; if (SYNC & 0x2) { // sync the second file lfs3_file_sync(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close the second file lfs3_file_close(&lfs3, &file__) => 0; // now it should show up // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // recieved sync broadcast? lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file___) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_unrelated] # different number of orphan require different methods of cleanup defines.N = 'range(6)' defines.M = 'range(6)' defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create N orphans lfs3_file_t orphans[N]; for (lfs3_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); lfs3_file_open(&lfs3, &orphans[o], name, LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; uint32_t prng = 42+o; for (lfs3_off_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, &orphans[o], wbuf, CHUNK) => CHUNK; } } // and an unrelated file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint32_t prng = 52; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, none of the orphans exist // via stat struct lfs3_info info; for (lfs3_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); lfs3_stat(&lfs3, name, &info) => LFS3_ERR_NOENT; } lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; uint8_t rbuf[CHUNK]; for (lfs3_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); // rdonly rejected lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, name, LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDWR) => LFS3_ERR_NOENT; } lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // but we should still be able to read our orphans for (lfs3_size_t o = 0; o < N; o++) { lfs3_file_rewind(&lfs3, &orphans[o]) => 0; prng = 42+o; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &orphans[o], rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } lfs3_file_rewind(&lfs3, &file) => 0; prng = 52; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close M orphans for (lfs3_size_t o = 0; o < M && o < N; o++) { lfs3_file_close(&lfs3, &orphans[o]) => 0; } // create a new unrelated file, this should trigger // and orphan cleanup lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; prng = 62; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } lfs3_file_close(&lfs3, &file__); // and now close our original unrelated file lfs3_file_close(&lfs3, &file) => 0; // and close the remaining orphans for (lfs3_size_t o = M; o < N; o++) { lfs3_file_close(&lfs3, &orphans[o]) => 0; } // now our file should exist, but none of our orphans // via stat for (lfs3_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); lfs3_stat(&lfs3, name, &info) => LFS3_ERR_NOENT; } lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, "catman", &info) => 0; assert(strcmp(info.name, "catman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "catman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open for (lfs3_size_t o = 0; o < N; o++) { char name[256]; sprintf(name, "aatman%03x", o); // rdonly rejected lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, name, LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, name, LFS3_O_RDWR) => LFS3_ERR_NOENT; } lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 52; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_file_open(&lfs3, &file_, "catman", LFS3_O_RDONLY) => 0; prng = 62; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_open] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the orphan lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_excl] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the zombie // // orphaned files will never exist again, so LFS3_O_EXCL should not // fail here // lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_mkdir] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new dir over the orphan lfs3_mkdir(&lfs3, "batman") => 0; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new dir is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_rm] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // orphans aren't real, so remove should fail lfs3_remove(&lfs3, "batman") => LFS3_ERR_NOENT; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // just make sure things look ok // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_mv_dst] defines.DIR = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // make our src file before orphans, otherwise open just cleans // things up if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "a/datman" : "datman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/datman" : "datman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; } // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, (INTERDIR) ? "c/aatman%03x" : "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename onto orphan lfs3_rename(&lfs3, (INTERDIR) ? "a/datman" : "datman", (INTERDIR) ? "c/batman" : "batman") => 0; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "c/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "c" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open if (DIR) { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "c/batman" : "batman", LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "c/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_orphan_mv_src] defines.ORPHANS = [1, 2, 3, 100] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create neighboring orphaned files // // more orphans requires different techniques for cleaning up orphans lfs3_file_t orphans[ORPHANS-1]; for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { char name[256]; sprintf(name, "aatman%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &orphans[i], "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // create an orphaned file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; lfs3_file_write(&lfs3, &file, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); // close all orphans at once, or else the open calls would just // clean up each orphans for (lfs3_size_t i = 0; i < ORPHANS-1; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_file_close(&lfs3, &file) => 0; if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // orphans aren't real, so rename should fail lfs3_rename(&lfs3, "batman", "catman") => LFS3_ERR_NOENT; if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // just make sure things look ok // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; lfs3_stat(&lfs3, "catman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that removed files never show up [cases.test_stickynotes_zombie] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, the file does not exist yet // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; // but we should still be able to read our file handle lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; // because the file was removed, it should still not exist // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // even after a remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_posthumous] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // as far as the filesystem is concerned, the file does not exist yet // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; // but we should still be able to read our file handle lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; // because the file was removed, it should still not exist // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // even after a remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_rwrw] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; // syncing our second file should actually sync lfs3_file_sync(&lfs3, &file__) => 0; // second file should appear on disk now // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // the perhaps surprising thing is we should still be able // to read both file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; lfs3_file_close(&lfs3, &file__) => 0; // check disk again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // check disk again again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_rwrw_posthumous] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the second file uint32_t prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // write to the first file prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; // syncing our second file should actually sync lfs3_file_sync(&lfs3, &file__) => 0; // second file should appear on disk now // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // the perhaps surprising thing is we should still be able // to read both file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; lfs3_file_close(&lfs3, &file__) => 0; // check disk again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // check disk again again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_zombie_rwrwrw] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // need to sync so we can remove lfs3_file_sync(&lfs3, &file__) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // still doesn't exist // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a third file lfs3_file_t file___; lfs3_file_open(&lfs3, &file___, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the third file prng = 42+2; for (lfs3_off_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, &file___, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; lfs3_file_sync(&lfs3, &file__) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; lfs3_file_resync(&lfs3, &file__) => LFS3_ERR_NOENT; // syncing our third file should actually sync lfs3_file_sync(&lfs3, &file___) => 0; // third file should appear on disk now // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // the perhaps surprising thing is we should still be able // to read all file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; lfs3_file_close(&lfs3, &file__) => 0; lfs3_file_close(&lfs3, &file___) => 0; // check disk again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // check disk again again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_zombie_rwrwrw_posthumous] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can remove lfs3_file_sync(&lfs3, &file) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // as far as the filesystem is concerned, this file does not exist anymore // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can remove lfs3_file_sync(&lfs3, &file__) => 0; // remove the file lfs3_remove(&lfs3, "batman") => 0; // still doesn't exist // via stat lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // rdonly rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => LFS3_ERR_NOENT; // non-create rejected lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_WRONLY) => LFS3_ERR_NOENT; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDWR) => LFS3_ERR_NOENT; // create a third file lfs3_file_t file___; lfs3_file_open(&lfs3, &file___, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the third file uint32_t prng = 42+2; for (lfs3_off_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, &file___, wbuf, CHUNK) => CHUNK; } // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // write to the first file prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // still appears as a stickynote // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open uint8_t rbuf[CHUNK]; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // syncing removed files is a noop lfs3_file_sync(&lfs3, &file) => 0; lfs3_file_sync(&lfs3, &file__) => 0; // resyncing removed files is an error lfs3_file_resync(&lfs3, &file) => LFS3_ERR_NOENT; lfs3_file_resync(&lfs3, &file__) => LFS3_ERR_NOENT; // syncing our third file should actually sync lfs3_file_sync(&lfs3, &file___) => 0; // third file should appear on disk now // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // the perhaps surprising thing is we should still be able // to read all file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file___) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file___, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; lfs3_file_close(&lfs3, &file__) => 0; lfs3_file_close(&lfs3, &file___) => 0; // check disk again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // check disk again again // via stat lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // via readdir lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; prng = 42+2; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_open] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the zombie lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_excl] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new file over the zombie // // zombie files will never exist again, so LFS3_O_EXCL should not // fail here // lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_mkdir] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // create a new dir over the zombie lfs3_mkdir(&lfs3, "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new dir is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_rm] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // zombies aren't real, so remove should fail lfs3_remove(&lfs3, "batman") => LFS3_ERR_NOENT; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // just make sure things look ok // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_mv_dst] defines.DIR = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // make our src file before zombies, just in case if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; } // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename onto zombie lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open if (DIR) { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; } if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombie_mv_src] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // zombies aren't real, so rename should fail lfs3_rename(&lfs3, "batman", "catman") => LFS3_ERR_NOENT; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // just make sure things look ok // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; lfs3_stat(&lfs3, "catman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test some other operations that can make zombies [cases.test_stickynotes_zombify_mkdir] defines.POSTHUMOUS = [false, true] defines.CLOSE = [false, true] defines.REMOUNT = [false, true] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a desync file, not a zombie yet lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // use mkdir on the same name, the file hasn't been created yet, // so this shouldn't fail, but because we have an open file handle // we create a zombie lfs3_mkdir(&lfs3, "batman") => 0; if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new dir is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; if (!CLOSE) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_zombify_mv_dst] defines.ORPHAN = [false, true] defines.DIR = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.POSTHUMOUS = [false, true] defines.CLOSE = [false, true] defines.REMOUNT = [false, true] defines.MKCONSISTENT = [false, true] if = [ 'REMOUNT <= CLOSE', '!DIR || ORPHAN', ] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // make our src file before zombies, just in case if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; } // create a desync file, not a zombie yet lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } if (!ORPHAN) { lfs3_file_sync(&lfs3, &zombie) => 0; } // rename onto the same name, this shouldn't fail (note the test // conditions), but because we have an open file handle we create // a zombie lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open if (DIR) { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; } if (!CLOSE) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_fileonzombie_rm] defines.DIR = [false, true] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, "batman") => 0; // create a file on top of the zombie if (DIR) { lfs3_mkdir(&lfs3, "batman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // remove the file lfs3_remove(&lfs3, "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // just make sure things look ok // via stat struct lfs3_info info; lfs3_stat(&lfs3, "batman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_fileonzombie_mv_dst] defines.DIR = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // make our src file before zombies, just in case if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; } // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0; // create a file on top of the zombie if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename onto the file lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our zombie lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } lfs3_stat(&lfs3, (INTERDIR) ? "a/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open if (DIR) { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; } if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' # test that we actually cleanup orphans correctly [cases.test_stickynotes_cleanup] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # <=2 => grm-able # >2 => requires orphans defines.N = [0, 1, 2, 3, 10, 100] defines.REMOUNT = [false, true] defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3] if = '(SIZE*N)/BLOCK_SIZE <= 32' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create some unrelated files to make sure cleaning up orphans // doesn't break other filesystem things uint32_t bookend_prngs[2] = {0, 0}; if (BOOKENDS & 0x1) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "aatman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[0] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } if (BOOKENDS & 0x2) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "catman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[1] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t files[N]; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "batman%03x", i); lfs3_file_open(&lfs3, &files[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } // remount? this has no effect on orphans if (REMOUNT) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } // calling lfs3_fs_mkconsistent should clean things up lfs3_fs_mkconsistent(&lfs3) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here assert(lfs3_mtree_weight(&lfs3) <= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits)); lfs3_mdir_t mdir; lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0; assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS)); // check that other files are unaffected for (int remount = 0; remount < 2; remount++) { if (remount) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (BOOKENDS & 0x1) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "aatman", LFS3_O_RDONLY) => 0; uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file); } if (BOOKENDS & 0x2) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, "catman", LFS3_O_RDONLY) => 0; uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file); } } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_cleanup_open] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # <=2 => grm-able # >2 => requires orphans defines.N = [0, 1, 2, 3, 10, 100] defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3] if = '(SIZE*N)/BLOCK_SIZE <= 32' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create some unrelated opened files to make sure cleaning up // orphans doesn't break other filesystem things // // note we leave these open in this test lfs3_file_t bookend_files[2]; uint32_t bookend_prngs[2] = {0, 0}; if (BOOKENDS & 0x1) { lfs3_file_open(&lfs3, &bookend_files[0], "aatman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[0] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &bookend_files[0]) => 0; } if (BOOKENDS & 0x2) { lfs3_file_open(&lfs3, &bookend_files[1], "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[1] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &bookend_files[1]) => 0; } // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t files[N]; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "batman%03x", i); lfs3_file_open(&lfs3, &files[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } // calling lfs3_fs_mkconsistent should clean things up lfs3_fs_mkconsistent(&lfs3) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here assert(lfs3_mtree_weight(&lfs3) <= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits)); lfs3_mdir_t mdir; lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0; assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS)); // check that other files are unaffected for (int remount = 0; remount < 2; remount++) { if (remount) { if (BOOKENDS & 0x1) { lfs3_file_close(&lfs3, &bookend_files[0]) => 0; } if (BOOKENDS & 0x2) { lfs3_file_close(&lfs3, &bookend_files[1]) => 0; } lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; if (BOOKENDS & 0x1) { lfs3_file_open(&lfs3, &bookend_files[0], "aatman", LFS3_O_RDONLY) => 0; } if (BOOKENDS & 0x2) { lfs3_file_open(&lfs3, &bookend_files[1], "catman", LFS3_O_RDONLY) => 0; } } if (BOOKENDS & 0x1) { lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0; uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } if (BOOKENDS & 0x2) { lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0; uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } if (BOOKENDS & 0x1) { lfs3_file_close(&lfs3, &bookend_files[0]); } if (BOOKENDS & 0x2) { lfs3_file_close(&lfs3, &bookend_files[1]); } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_cleanup_uncreat] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # <=2 => grm-able # >2 => requires orphans defines.N = [0, 1, 2, 3, 10, 100] defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3] if = '(SIZE*N)/BLOCK_SIZE <= 32' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create some unrelated uncreat files to make sure cleaning up // orphans doesn't break other filesystem things // // note we leave these uncreated + open in this test lfs3_file_t bookend_files[2]; uint32_t bookend_prngs[2] = {0, 0}; if (BOOKENDS & 0x1) { lfs3_file_open(&lfs3, &bookend_files[0], "aatman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[0] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE; } if (BOOKENDS & 0x2) { lfs3_file_open(&lfs3, &bookend_files[1], "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[1] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE; } // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t files[N]; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "batman%03x", i); lfs3_file_open(&lfs3, &files[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } // calling lfs3_fs_mkconsistent should clean things up lfs3_fs_mkconsistent(&lfs3) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here assert(lfs3_mtree_weight(&lfs3) <= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits)); lfs3_mdir_t mdir; lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0; assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS)); // check that other files are unaffected for (int remount = 0; remount < 2; remount++) { if (remount) { if (BOOKENDS & 0x1) { lfs3_file_close(&lfs3, &bookend_files[0]) => 0; } if (BOOKENDS & 0x2) { lfs3_file_close(&lfs3, &bookend_files[1]) => 0; } lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; if (BOOKENDS & 0x1) { lfs3_file_open(&lfs3, &bookend_files[0], "aatman", LFS3_O_RDONLY) => 0; } if (BOOKENDS & 0x2) { lfs3_file_open(&lfs3, &bookend_files[1], "catman", LFS3_O_RDONLY) => 0; } } if (BOOKENDS & 0x1) { lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0; uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } if (BOOKENDS & 0x2) { lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0; uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } if (BOOKENDS & 0x1) { lfs3_file_close(&lfs3, &bookend_files[0]); } if (BOOKENDS & 0x2) { lfs3_file_close(&lfs3, &bookend_files[1]); } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_cleanup_zombie] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # <=2 => grm-able # >2 => requires orphans defines.N = [0, 1, 2, 3, 10, 100] defines.BOOKENDS = [0x0, 0x1, 0x2, 0x3] if = '(SIZE*N)/BLOCK_SIZE <= 32' in = 'lfs3.c' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; uint32_t prng = 42; // create some unrelated zombie files to make sure cleaning up // orphans doesn't break other filesystem things // // note we leave these zombied + open in this test lfs3_file_t bookend_files[2]; uint32_t bookend_prngs[2] = {0, 0}; if (BOOKENDS & 0x1) { lfs3_file_open(&lfs3, &bookend_files[0], "aatman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[0] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[0], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &bookend_files[0]) => 0; lfs3_remove(&lfs3, "aatman") => 0; } if (BOOKENDS & 0x2) { lfs3_file_open(&lfs3, &bookend_files[1], "catman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; bookend_prngs[1] = TEST_PRNG(&prng); uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } lfs3_file_write(&lfs3, &bookend_files[1], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &bookend_files[1]) => 0; lfs3_remove(&lfs3, "catman") => 0; } // create this many orphaned files // // anytime we close a not-yet-created desync file, we create an // orphan, but note we need these to be different files, and we need // to close them after all open calls, otherwise we just end up with // one orphan (littlefs is eager to clean up orphans) // lfs3_file_t files[N]; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "batman%03x", i); lfs3_file_open(&lfs3, &files[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } // calling lfs3_fs_mkconsistent should clean things up lfs3_fs_mkconsistent(&lfs3) => 0; // we should have cleaned up all grms/orphans assert(lfs3.grm.queue[0] == 0); assert(lfs3.grm.queue[1] == 0); assert(!(lfs3.flags & LFS3_I_MKCONSISTENT)); struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(!(fsinfo.flags & LFS3_I_MKCONSISTENT)); // double check the actual disk state, it's easy for littlefs to // lie here assert(lfs3_mtree_weight(&lfs3) <= ((1+lfs3_popc(BOOKENDS)) << lfs3.mbits)); lfs3_mdir_t mdir; lfs3_mtree_lookup(&lfs3, 0, &mdir) => 0; assert(mdir.r.weight <= 1+lfs3_popc(BOOKENDS)); // check that other files are unaffected if (BOOKENDS & 0x1) { lfs3_file_rewind(&lfs3, &bookend_files[0]) => 0; uint32_t prng_ = bookend_prngs[0]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[0], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } if (BOOKENDS & 0x2) { lfs3_file_rewind(&lfs3, &bookend_files[1]) => 0; uint32_t prng_ = bookend_prngs[1]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng_) % 26); } uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &bookend_files[1], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } if (BOOKENDS & 0x1) { lfs3_file_close(&lfs3, &bookend_files[0]); } if (BOOKENDS & 0x2) { lfs3_file_close(&lfs3, &bookend_files[1]); } lfs3_unmount(&lfs3) => 0; ''' # test that file handles follow mvs correctly # # these doesn't really involve stickynotes, but it's the same internal # circuitry, we might as well test them here # [cases.test_stickynotes_file_mv] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // need to sync so we can rename lfs3_file_sync(&lfs3, &file) => 0; // rename the file lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // the file should have been renamed // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // but we should still be able to read our file handle lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } // close lfs3_file_close(&lfs3, &file) => 0; // close should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // remount should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_mv_postmv] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can rename lfs3_file_sync(&lfs3, &file) => 0; // rename the file lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // the file should have been renamed, but zero sized // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // but we should still be able to read our file handle lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } if (SYNC) { // sync lfs3_file_sync(&lfs3, &file) => 0; // the data should now be visible // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // we should still be able to read our file handle lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close lfs3_file_close(&lfs3, &file) => 0; // the data should now be visible // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // remount should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_mv_file_rwrw] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // write to the file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // need to sync so we can rename lfs3_file_sync(&lfs3, &file) => 0; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_TRUNC) => 0; // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // rename the file lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // the file should have been renamed // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // but we should still be able to read both file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } if (SYNC) { // sync, note the order lfs3_file_sync(&lfs3, &file__) => 0; lfs3_file_sync(&lfs3, &file) => 0; // the second file's data should now be visible // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // both file handles should be updated lfs3_file_rewind(&lfs3, &file) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close, note the order lfs3_file_close(&lfs3, &file__) => 0; lfs3_file_close(&lfs3, &file) => 0; // close should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // remount should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_mv_rwrw_postmv] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = 'LFS3_MIN(64, SIZE)' defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.SYNC = [false, true] defines.MKCONSISTENT = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // need to sync so we can rename lfs3_file_sync(&lfs3, &file) => 0; // create a second file lfs3_file_t file__; lfs3_file_open(&lfs3, &file__, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_TRUNC) => 0; // rename the file lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; // write to the first file uint32_t prng = 42; for (lfs3_off_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, &file, wbuf, CHUNK) => CHUNK; } // write to the second file prng = 42+1; for (lfs3_off_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, &file__, wbuf, CHUNK) => CHUNK; } // mkconsistent should have no effect if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // the file should have been renamed, but zero sized // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => 0; lfs3_file_close(&lfs3, &file_) => 0; // but we should still be able to read both file handles lfs3_file_rewind(&lfs3, &file) => 0; prng = 42; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } if (SYNC) { // sync, note the order lfs3_file_sync(&lfs3, &file__) => 0; lfs3_file_sync(&lfs3, &file) => 0; // the second file's data should now be visible // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // both file handles should be updated lfs3_file_rewind(&lfs3, &file) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_rewind(&lfs3, &file__) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file__, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } } // close, note the order lfs3_file_close(&lfs3, &file__) => 0; lfs3_file_close(&lfs3, &file) => 0; // close should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; // remount lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // remount should have no effect // via stat lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; prng = 42+1; for (lfs3_off_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); } uint8_t rbuf[CHUNK]; lfs3_file_read(&lfs3, &file_, rbuf, CHUNK) => CHUNK; assert(memcmp(wbuf, rbuf, CHUNK) == 0); } lfs3_file_close(&lfs3, &file_) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_mv_mv_src] defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.POSTMV = [false, true] defines.SYNC = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } // create a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_sync(&lfs3, &file) => 0; if (!POSTMV) { lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } } if (CLOSE == 2) { lfs3_file_close(&lfs3, &file) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename while open lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTMV) { lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); if (SYNC) { lfs3_file_sync(&lfs3, &file) => 0; } } // we should still be able to read our file lfs3_file_rewind(&lfs3, &file) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &file) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) { assert(info.size == strlen("catman!")); } else { assert(info.size == 0); } lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); assert(info.type == LFS3_TYPE_REG); if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) { assert(info.size == strlen("catman!")); } else { assert(info.size == 0); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; if ((SYNC || CLOSE >= 1) && !(POSTMV && CLOSE >= 2)) { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); } else { lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => 0; } lfs3_file_close(&lfs3, &file_) => 0; if (CLOSE == 0) { lfs3_file_close(&lfs3, &file) => 0; } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_fileonzombie_mv_src] defines.DIR = [false, true] defines.EXISTS = [false, true] defines.INTERDIR = [false, true] defines.DISTANCE = [0, 1, 100] defines.POSTHUMOUS = [false, true] # CLOSE=0 => don't close (before end of test) # CLOSE=1 => close after op # CLOSE=2 => close before op defines.CLOSE = [0, 1, 2] # REMOUNT=0 => don't remount # REMOUNT=1 => remount after op # REMOUNT=2 => remount before op defines.REMOUNT = [0, 1, 2] defines.MKCONSISTENT = [false, true] if = 'REMOUNT <= CLOSE' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting directory structure if (INTERDIR) { lfs3_mkdir(&lfs3, "a") => 0; lfs3_mkdir(&lfs3, "b") => 0; lfs3_mkdir(&lfs3, "c") => 0; } for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "b/catman%03x" : "catman%03x", i); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_close(&lfs3, &file) => 0; } // create our destination first, just in case if (EXISTS) { if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "a/batman" : "batman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "a/batman" : "batman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hmmmmmmmm", strlen("hmmmmmmmm")) => strlen("hmmmmmmmm"); lfs3_file_close(&lfs3, &file) => 0; } } // create a zombie lfs3_file_t zombie; lfs3_file_open(&lfs3, &zombie, (INTERDIR) ? "c/datman" : "datman", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; if (!POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } lfs3_file_sync(&lfs3, &zombie) => 0; lfs3_remove(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0; // create a file on top of the zombie if (DIR) { lfs3_mkdir(&lfs3, (INTERDIR) ? "c/datman" : "datman") => 0; } else { lfs3_file_t file; lfs3_file_open(&lfs3, &file, (INTERDIR) ? "c/datman" : "datman", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "catman!", strlen("catman!")) => strlen("catman!"); lfs3_file_close(&lfs3, &file) => 0; } if (CLOSE == 2) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 2) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // rename while open lfs3_rename(&lfs3, (INTERDIR) ? "c/datman" : "datman", (INTERDIR) ? "a/batman" : "batman") => 0; if (CLOSE <= 1 && REMOUNT <= 1) { if (POSTHUMOUS) { lfs3_file_write(&lfs3, &zombie, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) => strlen("WoOoOoOoOoO"); } // we should still be able to read our file lfs3_file_rewind(&lfs3, &zombie) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &zombie, rbuf, sizeof(rbuf)) => strlen("WoOoOoOoOoO"); assert(memcmp(rbuf, "WoOoOoOoOoO", strlen("WoOoOoOoOoO")) == 0); if (CLOSE == 1) { lfs3_file_close(&lfs3, &zombie) => 0; } if (REMOUNT == 1) { lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; } } if (MKCONSISTENT) { lfs3_fs_mkconsistent(&lfs3) => 0; } // make sure the new file is readable // via stat struct lfs3_info info; lfs3_stat(&lfs3, (INTERDIR) ? "a/batman" : "batman", &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } lfs3_stat(&lfs3, (INTERDIR) ? "c/datman" : "datman", &info) => LFS3_ERR_NOENT; // via readdir lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, (INTERDIR) ? "a" : "/") => 0; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "batman") == 0); if (DIR) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("catman!")); } if (!INTERDIR) { for (lfs3_size_t i = 0; i < DISTANCE; i++) { char name[256]; sprintf(name, (INTERDIR) ? "a/catman%03x" : "catman%03x", i); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == 0); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // via open if (DIR) { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { lfs3_file_t file_; lfs3_file_open(&lfs3, &file_, (INTERDIR) ? "a/batman" : "batman", LFS3_O_RDONLY) => 0; uint8_t rbuf[256]; lfs3_file_read(&lfs3, &file_, rbuf, sizeof(rbuf)) => strlen("catman!"); assert(memcmp(rbuf, "catman!", strlen("catman!")) == 0); lfs3_file_close(&lfs3, &file_) => 0; } if (CLOSE == 0) { lfs3_file_close(&lfs3, &zombie) => 0; } lfs3_unmount(&lfs3) => 0; ''' # one particularly nasty case is renaming over an mdir split, since shrubs # can be moved around quite a few times when that happens # # here we spam renames over an increasing number of files to hopefully hit # that case # [cases.test_stickynotes_file_mv_split] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # keep unsynced data open? defines.UNSYNC = [false, true] # keep a desynced file open? defines.DESYNC = [false, true] # keep a zombie open? defines.ZOMBIE = [false, true] if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // we need a file handle for each file + desync + zombie lfs3_file_t files[N]; lfs3_file_t desyncs[N]; lfs3_file_t zombies[N]; // create this many files while renaming uint32_t prng = 42; uint32_t unsync_prng = 43; uint32_t desync_prng = 44; uint32_t zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // create a zombie? if (ZOMBIE) { lfs3_file_open(&lfs3, &zombies[i], "batman!!!", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } lfs3_file_write(&lfs3, &zombies[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &zombies[i]) => 0; lfs3_remove(&lfs3, "batman!!!") => 0; } // always create as first file lfs3_file_open(&lfs3, &files[i], "batman!!!", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &files[i]) => 0; // keep unsynced data? if (UNSYNC) { lfs3_file_rewind(&lfs3, &files[i]) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // keep a desynced file open? if (DESYNC) { lfs3_file_open(&lfs3, &desyncs[i], "batman!!!", LFS3_O_RDWR | LFS3_O_DESYNC | LFS3_O_TRUNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } lfs3_file_write(&lfs3, &desyncs[i], wbuf, SIZE) => SIZE; } // rename! char name[256]; sprintf(name, "batman%03x", i); lfs3_rename(&lfs3, "batman!!!", name) => 0; } // check that renames worked prng = 42; struct lfs3_info info; lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", i); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected prng = (!UNSYNC) ? 42 : 43; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // try syncing any unsynced files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_sync(&lfs3, &files[i]) => 0; } // check that sync worked prng = (!UNSYNC) ? 42 : 43; lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", i); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected prng = (!UNSYNC) ? 42 : 43; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // try rewriting our open files uint32_t rewrite_prng = 52; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_rewind(&lfs3, &files[i]) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &files[i]) => 0; } // check that rewrites worked rewrite_prng = 52; lfs3_stat(&lfs3, "batman!!!", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", i); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected rewrite_prng = 52; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // cleanup files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } if (DESYNC) { for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &desyncs[i]) => 0; } } if (ZOMBIE) { for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &zombies[i]) => 0; } } lfs3_unmount(&lfs3) => 0; ''' [cases.test_stickynotes_file_mv_split_backwards] defines.N = [1, 2, 4, 8, 16, 32, 64] defines.SIZE = [ '0', 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] # keep unsynced data open? defines.UNSYNC = [false, true] # keep a desynced file open? defines.DESYNC = [false, true] # keep a zombie open? defines.ZOMBIE = [false, true] if = '(SIZE*N*(1+DESYNC+ZOMBIE))/BLOCK_SIZE <= 32' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // we need a file handle for each file + desync + zombie lfs3_file_t files[N]; lfs3_file_t desyncs[N]; lfs3_file_t zombies[N]; // create this many files while renaming uint32_t prng = 42; uint32_t unsync_prng = 43; uint32_t desync_prng = 44; uint32_t zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // create a zombie? if (ZOMBIE) { lfs3_file_open(&lfs3, &zombies[i], "batman???", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } lfs3_file_write(&lfs3, &zombies[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &zombies[i]) => 0; lfs3_remove(&lfs3, "batman???") => 0; } // always create as last file lfs3_file_open(&lfs3, &files[i], "batman???", LFS3_O_RDWR | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &files[i]) => 0; // keep unsynced data? if (UNSYNC) { lfs3_file_rewind(&lfs3, &files[i]) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&unsync_prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; } // keep a desynced file open? if (DESYNC) { lfs3_file_open(&lfs3, &desyncs[i], "batman???", LFS3_O_RDWR | LFS3_O_DESYNC | LFS3_O_TRUNC) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } lfs3_file_write(&lfs3, &desyncs[i], wbuf, SIZE) => SIZE; } // rename! char name[256]; sprintf(name, "batman%03x", (uint32_t)(N-1-i)); lfs3_rename(&lfs3, "batman???", name) => 0; } // check that renames worked prng = 42; struct lfs3_info info; lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", (uint32_t)(N-1-i)); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected prng = (!UNSYNC) ? 42 : 43; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // try syncing any unsynced files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_sync(&lfs3, &files[i]) => 0; } // check that sync worked prng = (!UNSYNC) ? 42 : 43; lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", (uint32_t)(N-1-i)); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected prng = (!UNSYNC) ? 42 : 43; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // try rewriting our open files uint32_t rewrite_prng = 52; for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_rewind(&lfs3, &files[i]) => 0; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } lfs3_file_write(&lfs3, &files[i], wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &files[i]) => 0; } // check that rewrites worked rewrite_prng = 52; lfs3_stat(&lfs3, "batman???", &info) => LFS3_ERR_NOENT; for (lfs3_size_t i = 0; i < N; i++) { // check with stat char name[256]; sprintf(name, "batman%03x", (uint32_t)(N-1-i)); lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } lfs3_file_t file; uint8_t rbuf[SIZE]; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; } // check that file handles are as expected rewrite_prng = 52; desync_prng = 44; zombie_prng = 45; for (lfs3_size_t i = 0; i < N; i++) { // check size assert(lfs3_file_size(&lfs3, &files[i]) == SIZE); // try reading the file uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&rewrite_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &files[i]) => 0; lfs3_file_read(&lfs3, &files[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // check any desynced files if (DESYNC) { assert(lfs3_file_size(&lfs3, &desyncs[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&desync_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &desyncs[i]) => 0; lfs3_file_read(&lfs3, &desyncs[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // check any zombied files if (ZOMBIE) { assert(lfs3_file_size(&lfs3, &zombies[i]) == SIZE); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&zombie_prng) % 26); } uint8_t rbuf[SIZE]; lfs3_file_rewind(&lfs3, &zombies[i]) => 0; lfs3_file_read(&lfs3, &zombies[i], rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } // cleanup files for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &files[i]) => 0; } if (DESYNC) { for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &desyncs[i]) => 0; } } if (ZOMBIE) { for (lfs3_size_t i = 0; i < N; i++) { lfs3_file_close(&lfs3, &zombies[i]) => 0; } } lfs3_unmount(&lfs3) => 0; ''' # fuzz tests involving many uncreats + zombies, this gets a bit crazy [cases.test_stickynotes_uz_fuzz] # you probably need to flush if you expect errors defines.FLUSH = false defines.N = [1, 2, 4, 8, 16, 32, 64] # do more ops than files to encourage file rewrites defines.OPS = '2*N' defines.SIZE = [ '0', 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(20)' fuzz = 'SEED' if = '(SIZE*N)/BLOCK_SIZE <= 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); bool *sim_isstickys = malloc(N*sizeof(bool)); lfs3_size_t sim_size = 0; typedef struct sim_file { lfs3_size_t x; bool sticky; bool zombie; uint32_t prng; lfs3_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs3_size_t sim_file_count = 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 5; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; // already exists? bool exist = false; uint32_t wprng = 0; bool sticky = true; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { exist = true; wprng = sim_prngs[j]; sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist if (!exist) { wprng = TEST_PRNG(&prng); sticky = true; } lfs3_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfs3_file_open(&lfs3, &sim_files[j]->file, name, LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0; // write some initial data if we don't exist if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // open in our sim sim_files[j]->x = x; sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; } break; } } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // write to the file lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0; // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // update in our sim for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { // new prng sim_prngs[k] = wprng; // no longer sticky sim_isstickys[k] = false; break; } } // update related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { // new prng sim_files[k]->prng = wprng; // no longer sticky sim_files[k]->sticky = false; } } } // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; bool sticky = sim_files[j]->sticky; bool zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0; lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; // clobber closed files to try to catch lingering references memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // update our sim if (sticky && !zombie) { // orphaned? bool orphan = true; for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { orphan = false; } } // if we were never synced, delete from sim if (orphan) { for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { memmove(&sim[k], &sim[k+1], (sim_size-(k+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[k], &sim_prngs[k+1], (sim_size-(k+1))*sizeof(uint32_t)); memmove(&sim_isstickys[k], &sim_isstickys[k+1], (sim_size-(k+1))*sizeof(bool)); sim_size -= 1; break; } } } } // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete this file char name[256]; sprintf(name, "batman%03x", x); lfs3_remove(&lfs3, name) => 0; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; bool sticky = sim_isstickys[j]; // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; // update our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng/sticky sim_prngs[k] = wprng; sim_isstickys[k] = sticky; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; } break; } } // update any related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } } } // check that disk matches our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; if (sim_isstickys[j]) { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0; } else { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; } // check that our file handles match our simulation for (lfs3_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs3 free(sim); free(sim_prngs); free(sim_isstickys); for (lfs3_size_t j = 0; j < sim_file_count; j++) { lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfs3_unmount(&lfs3) => 0; ''' # fuzz tests involving many uncreats + zombies + dirs, this gets a bit crazy [cases.test_stickynotes_uzd_fuzz] # you probably need to flush if you expect errors defines.FLUSH = false defines.N = [1, 2, 4, 8, 16, 32, 64] # do more ops than files to encourage file rewrites defines.OPS = '2*N' defines.SIZE = [ '0', 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.SEED = 'range(20)' fuzz = 'SEED' if = '(SIZE*N)/BLOCK_SIZE <= 16' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // set up a simulation to compare against lfs3_size_t *sim = malloc(N*sizeof(lfs3_size_t)); uint32_t *sim_prngs = malloc(N*sizeof(uint32_t)); bool *sim_isstickys = malloc(N*sizeof(bool)); bool *sim_isdirs = malloc(N*sizeof(bool)); lfs3_size_t sim_size = 0; typedef struct sim_file { lfs3_size_t x; bool sticky; bool zombie; uint32_t prng; lfs3_file_t file; } sim_file_t; sim_file_t **sim_files = malloc(N*sizeof(sim_file_t*)); lfs3_size_t sim_file_count = 0; uint32_t prng = SEED; for (lfs3_size_t i = 0; i < OPS; i++) { nonsense:; // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 8; // open a new file? if (op == 0) { if (sim_file_count >= N) { goto nonsense; } // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; // already exists? bool exist = true; uint32_t wprng = 0; bool sticky = true; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim[j] == x) { if (sim_isdirs[j]) { goto nonsense; } exist = true; wprng = sim_prngs[j]; sticky = sim_isstickys[j]; break; } } // choose a random seed if we don't exist if (!exist) { wprng = TEST_PRNG(&prng); sticky = true; } lfs3_size_t j = sim_file_count; sim_files[j] = malloc(sizeof(sim_file_t)); // open the actual file char name[256]; sprintf(name, "batman%03x", x); lfs3_file_open(&lfs3, &sim_files[j]->file, name, LFS3_O_RDWR | LFS3_O_CREAT | ((FLUSH) ? LFS3_O_FLUSH : 0)) => 0; // write some initial data if we don't exist if (!exist || sticky) { uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; } // open in our sim sim_files[j]->x = x; sim_files[j]->sticky = sticky; sim_files[j]->zombie = false; sim_files[j]->prng = wprng; sim_file_count++; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { // new prng sim_prngs[k] = wprng; } else { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = false; } break; } } // write/rewrite a file? } else if (op == 1) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; // choose a random seed uint32_t wprng = TEST_PRNG(&prng); // write to the file lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t wbuf[SIZE]; uint32_t wprng_ = wprng; for (lfs3_size_t k = 0; k < SIZE; k++) { wbuf[k] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfs3_file_write(&lfs3, &sim_files[j]->file, wbuf, SIZE) => SIZE; lfs3_file_sync(&lfs3, &sim_files[j]->file) => 0; // update sim sim_files[j]->prng = wprng; if (!sim_files[j]->zombie) { // update in our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // new prng sim_prngs[k] = wprng; // no longer sticky sim_isstickys[k] = false; break; } } // update related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { // new prng sim_files[k]->prng = wprng; // no longer sticky sim_files[k]->sticky = false; } } } // close a file? } else if (op == 2) { if (sim_file_count == 0) { goto nonsense; } // choose a random file handle lfs3_size_t j = TEST_PRNG(&prng) % sim_file_count; lfs3_size_t x = sim_files[j]->x; lfs3_size_t sticky = sim_files[j]->sticky; lfs3_size_t zombie = sim_files[j]->zombie; // this doesn't really test anything, but if we don't close // files eventually everything will end up zombies // close the file without affected disk lfs3_file_desync(&lfs3, &sim_files[j]->file) => 0; lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; // clobber closed files to try to catch lingering references memset(&sim_files[j]->file, 0xcc, sizeof(lfs3_file_t)); // remove from list free(sim_files[j]); sim_files[j] = sim_files[sim_file_count-1]; sim_file_count -= 1; // update our sim if (sticky && !zombie) { // orphaned? bool orphan = true; for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x && !sim_files[k]->zombie) { orphan = false; } } // if we were never synced, delete from sim if (orphan) { for (lfs3_size_t k = 0;; k++) { if (sim[k] == x) { memmove(&sim[k], &sim[k+1], (sim_size-(k+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[k], &sim_prngs[k+1], (sim_size-(k+1))*sizeof(uint32_t)); memmove(&sim_isstickys[k], &sim_isstickys[k+1], (sim_size-(k+1))*sizeof(bool)); memmove(&sim_isdirs[k], &sim_isdirs[k+1], (sim_size-(k+1))*sizeof(bool)); sim_size -= 1; break; } } } } // remove a file? } else if (op == 3) { if (sim_size == 0) { goto nonsense; } // choose a random file to delete lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; // delete this file char name[256]; sprintf(name, "batman%03x", x); lfs3_remove(&lfs3, name) => 0; // delete from our sim memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } // rename a file? } else if (op == 4) { if (sim_size == 0) { goto nonsense; } // choose a random file to rename, and a random number to // rename to lfs3_size_t j = TEST_PRNG(&prng) % sim_size; lfs3_size_t x = sim[j]; lfs3_size_t y = TEST_PRNG(&prng) % N; uint32_t wprng = sim_prngs[j]; bool sticky = sim_isstickys[j]; bool dir = sim_isdirs[j]; for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // type mismatch? if (sim_isdirs[k] != dir) { goto nonsense; } } break; } } // rename this file char old_name[256]; sprintf(old_name, "batman%03x", x); char new_name[256]; sprintf(new_name, "batman%03x", y); lfs3_rename(&lfs3, old_name, new_name) => 0; // update our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= y) { // renaming and replacing if (k < sim_size && sim[k] == y && x != y) { // delete the original entry memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); sim_size -= 1; if (k > j) { k -= 1; } // update the prng/sticky/dir sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = dir; // just renaming } else { // first delete memmove(&sim[j], &sim[j+1], (sim_size-(j+1))*sizeof(lfs3_size_t)); memmove(&sim_prngs[j], &sim_prngs[j+1], (sim_size-(j+1))*sizeof(uint32_t)); memmove(&sim_isstickys[j], &sim_isstickys[j+1], (sim_size-(j+1))*sizeof(bool)); memmove(&sim_isdirs[j], &sim_isdirs[j+1], (sim_size-(j+1))*sizeof(bool)); if (k > j) { k -= 1; } // then insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim[k] = y; sim_prngs[k] = wprng; sim_isstickys[k] = sticky; sim_isdirs[k] = dir; } break; } } // update any related sim files for (lfs3_size_t k = 0; k < sim_file_count; k++) { // move source files if (sim_files[k]->x == x) { sim_files[k]->x = y; // mark target files as zombied } else if (sim_files[k]->x == y) { sim_files[k]->zombie = true; } } // toss a directory into the mix } else if (op == 5) { // choose a pseudo-random number lfs3_size_t x = TEST_PRNG(&prng) % N; for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // already seen? if (k < sim_size && sim[k] == x) { goto nonsense; } break; } } // make the directory char name[256]; sprintf(name, "batman%03x", x); lfs3_mkdir(&lfs3, name) => 0; // insert into our sim for (lfs3_size_t k = 0;; k++) { if (k >= sim_size || sim[k] >= x) { // insert memmove(&sim[k+1], &sim[k], (sim_size-k)*sizeof(lfs3_size_t)); memmove(&sim_prngs[k+1], &sim_prngs[k], (sim_size-k)*sizeof(uint32_t)); memmove(&sim_isstickys[k+1], &sim_isstickys[k], (sim_size-k)*sizeof(bool)); memmove(&sim_isdirs[k+1], &sim_isdirs[k], (sim_size-k)*sizeof(bool)); sim_size += 1; sim[k] = x; sim_prngs[k] = 0; sim_isdirs[k] = true; break; } } // mark any related sim files as zombied for (lfs3_size_t k = 0; k < sim_file_count; k++) { if (sim_files[k]->x == x) { sim_files[k]->zombie = true; } } } } // check that disk matches our simulation for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); struct lfs3_info info; lfs3_stat(&lfs3, name, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_t dir; lfs3_dir_open(&lfs3, &dir, "/") => 0; struct lfs3_info info; lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, ".") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "..") == 0); assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); for (lfs3_size_t j = 0; j < sim_size; j++) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, name) == 0); if (sim_isdirs[j]) { assert(info.type == LFS3_TYPE_DIR); assert(info.size == 0); } else if (sim_isstickys[j]) { assert(info.type == LFS3_TYPE_STICKYNOTE); assert(info.size == 0); } else { assert(info.type == LFS3_TYPE_REG); assert(info.size == SIZE); } } lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; for (lfs3_size_t j = 0; j < sim_size; j++) { if (sim_isdirs[j]) { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => LFS3_ERR_ISDIR; } else { char name[256]; sprintf(name, "batman%03x", sim[j]); lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_RDONLY) => 0; uint32_t wprng = sim_prngs[j]; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } uint8_t rbuf[SIZE]; if (sim_isstickys[j]) { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => 0; } else { lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } lfs3_file_close(&lfs3, &file) => 0; } } // check that our file handles match our simulation for (lfs3_size_t j = 0; j < sim_file_count; j++) { uint32_t wprng = sim_files[j]->prng; uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfs3_file_rewind(&lfs3, &sim_files[j]->file) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &sim_files[j]->file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } // clean up sim/lfs3 free(sim); free(sim_prngs); free(sim_isstickys); free(sim_isdirs); for (lfs3_size_t j = 0; j < sim_file_count; j++) { lfs3_file_close(&lfs3, &sim_files[j]->file) => 0; free(sim_files[j]); } free(sim_files); lfs3_unmount(&lfs3) => 0; '''