# Advanced mount tests after = ['test_mtree', 'test_trvs'] # Test both with and without the gbmap if available defines.GBMAP = [false, true] if = 'LFS3_IFYES_GBMAP(GBMAP, true, !GBMAP)' # test we can mount [cases.test_mount_simple] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that various mount flags don't assert and are returned by # lfs3_fs_stat [cases.test_mount_flags] defines.RDONLY = [false, true] defines.FLUSH = [false, true] defines.SYNC = [false, true] defines.REVDBG = [false, true] defines.REVNOISE = [false, true] defines.CKPROGS = [false, true] defines.CKFETCHES = [false, true] defines.CKMETAPARITY = [false, true] defines.CKDATACKSUMS = [false, true] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = [ 'LFS3_IFYES_REVDBG(REVDBG, true, !REVDBG)', 'LFS3_IFYES_REVNOISE(REVNOISE, true, !REVNOISE)', 'LFS3_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS3_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS3_IFDEF_CKMETAPARITY(true, !CKMETAPARITY)', 'LFS3_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)', '!RDONLY || !MKCONSISTENT', '!RDONLY || !LOOKAHEAD', '!RDONLY || !COMPACT', ] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, ((RDONLY) ? LFS3_M_RDONLY : LFS3_M_RDWR) | ((FLUSH) ? LFS3_M_FLUSH : 0) | ((SYNC) ? LFS3_M_SYNC : 0) | ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_M_REVDBG, -1) : 0) | ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_M_REVNOISE, -1) : 0) | ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_M_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_M_CKFETCHES, -1) : 0) | ((CKMETAPARITY) ? LFS3_IFDEF_CKMETAPARITY(LFS3_M_CKMETAPARITY, -1) : 0) | ((CKDATACKSUMS) ? LFS3_IFDEF_CKDATACKSUMS(LFS3_M_CKDATACKSUMS, -1) : 0) | ((MKCONSISTENT) ? LFS3_M_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0) | ((COMPACT) ? LFS3_M_COMPACT : 0) | ((CKMETA) ? LFS3_M_CKMETA : 0) | ((CKDATA) ? LFS3_M_CKDATA : 0), CFG) => 0; // lfs3_fs_stat only returns some flags struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( ((RDONLY) ? LFS3_I_RDONLY : 0) | ((FLUSH) ? LFS3_I_FLUSH : 0) | ((SYNC) ? LFS3_I_SYNC : 0) | ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_I_REVDBG, -1) : 0) | ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_I_REVNOISE, -1) : 0) | ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_I_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_I_CKFETCHES, -1) : 0) | ((CKMETAPARITY) ? LFS3_IFDEF_CKMETAPARITY(LFS3_I_CKMETAPARITY, -1) : 0) | ((CKDATACKSUMS) ? LFS3_IFDEF_CKDATACKSUMS(LFS3_I_CKDATACKSUMS, -1) : 0) | ((!MKCONSISTENT) ? LFS3_I_MKCONSISTENT : 0) | ((!LOOKAHEAD && !GBMAP) ? LFS3_I_LOOKAHEAD : 0) | ((!COMPACT) ? LFS3_I_COMPACT : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' # test that various format flags don't assert or anything # # these end up passed to mount internally [cases.test_mount_format_flags] defines.REVDBG = [false, true] defines.REVNOISE = [false, true] defines.CKPROGS = [false, true] defines.CKFETCHES = [false, true] defines.CKMETAPARITY = [false, true] defines.CKDATACKSUMS = [false, true] defines.MKCONSISTENT = [false, true] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] if = [ 'LFS3_IFYES_REVDBG(REVDBG, true, !REVDBG)', 'LFS3_IFYES_REVNOISE(REVNOISE, true, !REVNOISE)', 'LFS3_IFDEF_CKPROGS(true, !CKPROGS)', 'LFS3_IFDEF_CKFETCHES(true, !CKFETCHES)', 'LFS3_IFDEF_CKMETAPARITY(true, !CKMETAPARITY)', 'LFS3_IFDEF_CKDATACKSUMS(true, !CKDATACKSUMS)', ] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((REVDBG) ? LFS3_IFDEF_REVDBG(LFS3_F_REVDBG, -1) : 0) | ((REVNOISE) ? LFS3_IFDEF_REVNOISE(LFS3_F_REVNOISE, -1) : 0) | ((CKPROGS) ? LFS3_IFDEF_CKPROGS(LFS3_F_CKPROGS, -1) : 0) | ((CKFETCHES) ? LFS3_IFDEF_CKFETCHES(LFS3_F_CKFETCHES, -1) : 0) | ((CKMETAPARITY) ? LFS3_IFDEF_CKMETAPARITY(LFS3_F_CKMETAPARITY, -1) : 0) | ((CKDATACKSUMS) ? LFS3_IFDEF_CKDATACKSUMS(LFS3_F_CKDATACKSUMS, -1) : 0) | ((MKCONSISTENT) ? LFS3_F_MKCONSISTENT : 0) | ((LOOKAHEAD) ? LFS3_F_LOOKAHEAD : 0) | ((COMPACT) ? LFS3_F_COMPACT : 0) | ((CKMETA) ? LFS3_F_CKMETA : 0) | ((CKDATA) ? LFS3_F_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // test that format-only flags are read correctly struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' # test that on-mount traversals do what they say they do [cases.test_mount_t_lookahead] defines.CKMETA = [false, true] defines.CKDATA = [false, true] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // by default we need a lookahead scan // // unless we have a gbmap, the gbmap is persistent so usually // _doesn't_ need a lookahead scan lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | ((!GBMAP) ? LFS3_I_LOOKAHEAD : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; // with LFS3_M_LOOKAHEAD, mount performs a lookahead scan lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_LOOKAHEAD | ((CKMETA) ? LFS3_M_CKMETA : 0) | ((CKDATA) ? LFS3_M_CKDATA : 0), CFG) => 0; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_t_lookgbmap] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = [ 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] ifdef = 'LFS3_GBMAP' if = 'GBMAP' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; uint32_t prng = 42; // gbmap is persistant, so by default we _don't_ need a gbmap scan lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // write to a file lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_WRONLY | 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, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // but if we allocated any blocks our gbmap will be inexhaustive lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | LFS3_I_LOOKAHEAD | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; // with LFS3_M_LOOKAHEAD, mount rebuild the gbmap lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_LOOKAHEAD | ((CKMETA) ? LFS3_M_CKMETA : 0) | ((CKDATA) ? LFS3_M_CKDATA : 0), CFG) => 0; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | LFS3_I_COMPACT // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_t_compact] defines.LOOKAHEAD = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = [ 'FCACHE_SIZE/2', '2*FCACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '8*BLOCK_SIZE', ] # set compact thresh to minimum defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; uint32_t prng = 42; // first lets create a compactable filesystem lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // write to our mdir until >gc_compact_thresh full lfs3_file_t file; lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; // hack, don't use the internals like this uint8_t wbuf[SIZE]; while ((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH) { lfs3_file_rewind(&lfs3, &file) => 0; 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_sync(&lfs3, &file) => 0; } lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // by default mount does not compact lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | ((!GBMAP || SIZE >= BLOCK_SIZE/4) ? LFS3_I_LOOKAHEAD : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; // with LFS3_M_COMPACT, mount compacts any uncompacted blocks lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_COMPACT | ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0) | ((CKMETA) ? LFS3_M_CKMETA : 0) | ((CKDATA) ? LFS3_M_CKDATA : 0), CFG) => 0; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | ((!LOOKAHEAD && (!GBMAP || SIZE >= BLOCK_SIZE/4)) ? LFS3_I_LOOKAHEAD : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // mdir should have been compacted lfs3_file_open(&lfs3, &file, "jellyfish", LFS3_O_RDONLY) => 0; assert((file.b.h.mdir.r.eoff & 0x7fffffff) <= GC_COMPACT_THRESH); // check we can still read the file uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_t_mkconsistent] defines.LOOKAHEAD = [false, true] defines.COMPACT = [false, true] defines.CKMETA = [false, true] defines.CKDATA = [false, true] defines.SIZE = 'FCACHE_SIZE/2' # <=2 => grm-able # >2 => requires orphans defines.ORPHANS = [0, 1, 2, 3, 100] code = ''' lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; uint32_t prng = 42; // first lets create some orphans lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create two files lfs3_file_t file; lfs3_file_open(&lfs3, &file, "cuttlefish", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf1[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf1[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf1, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "octopus", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; uint8_t wbuf2[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf2[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_write(&lfs3, &file, wbuf2, 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 orphans[ORPHANS]; for (lfs3_size_t i = 0; i < ORPHANS; i++) { char name[256]; sprintf(name, "jellyfish%03x", i); lfs3_file_open(&lfs3, &orphans[i], name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL | LFS3_O_DESYNC) => 0; } for (lfs3_size_t i = 0; i < ORPHANS; i++) { lfs3_file_close(&lfs3, &orphans[i]) => 0; } lfs3_unmount(&lfs3) => 0; // by default we clean up orphans lazily lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; struct lfs3_fsinfo fsinfo; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | LFS3_I_MKCONSISTENT | ((!GBMAP || ORPHANS >= 100) ? LFS3_I_LOOKAHEAD : 0) | LFS3_I_COMPACT | LFS3_I_CKMETA | LFS3_I_CKDATA | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); lfs3_unmount(&lfs3) => 0; // with LFS3_M_MKCONSISTENT, mount cleans up orphans eagerly lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_MKCONSISTENT | ((LOOKAHEAD) ? LFS3_M_LOOKAHEAD : 0) | ((COMPACT) ? LFS3_M_COMPACT : 0) | ((CKMETA) ? LFS3_M_CKMETA : 0) | ((CKDATA) ? LFS3_M_CKDATA : 0), CFG) => 0; lfs3_fs_stat(&lfs3, &fsinfo) => 0; assert(fsinfo.flags == ( LFS3_IFYES_REVDBG(LFS3_I_REVDBG, 0, 0) | LFS3_IFYES_REVNOISE(LFS3_I_REVNOISE, 0, 0) | ((!LOOKAHEAD && (!GBMAP || ORPHANS >= 100)) ? LFS3_I_LOOKAHEAD : 0) | ((!COMPACT) ? LFS3_I_COMPACT : 0) // note ckdata implies ckmeta | ((!CKMETA && !CKDATA) ? LFS3_I_CKMETA : 0) | ((!CKDATA) ? LFS3_I_CKDATA : 0) | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_I_GBMAP, -1) : 0))); // check we can still read the files lfs3_file_open(&lfs3, &file, "cuttlefish", LFS3_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf1, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "octopus", LFS3_O_RDONLY) => 0; lfs3_file_read(&lfs3, &file, rbuf, SIZE) => SIZE; assert(memcmp(rbuf, wbuf2, SIZE) == 0); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; ''' # test we can detect at least fully clobbered blocks # # these are tested more thoroughly in test_ck [cases.test_mount_t_ckmeta] 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', '8*BLOCK_SIZE', ] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_block_t i = 0; while (true) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting filesystem uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "squid%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } // this gets a bit tricky be cause we need to clobber both // blocks in mdir pairs if (tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE) { if (k == i || k == i+1) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; if (tinfo.btype != LFS3_BTYPE_MDIR || k == i+1) { i += (tinfo.btype == LFS3_BTYPE_MDIR) ? 2 : 1; lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto clobbered; } } k += 1; } } clobbered:; // mount with LFS3_M_CKMETA, we should detect clobbered blocks lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_CKMETA, CFG) => LFS3_ERR_CORRUPT; } done:; ''' [cases.test_mount_t_ckdata] 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', '8*BLOCK_SIZE', ] if = '(SIZE*N)/BLOCK_SIZE <= 32' code = ''' lfs3_block_t i = 0; while (true) { // a bit hacky, but this catches infinite loops assert(i < 2*BLOCK_COUNT); lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // create an interesting filesystem uint32_t prng = 42; for (lfs3_size_t i = 0; i < N; i++) { char name[256]; sprintf(name, "squid%03x", i); uint8_t wbuf[SIZE]; for (lfs3_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfs3_file_t file; lfs3_file_open(&lfs3, &file, name, LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE; lfs3_file_close(&lfs3, &file) => 0; } // traverse to find blocks lfs3_trv_t trv; lfs3_trv_open(&lfs3, &trv, LFS3_T_RDONLY) => 0; lfs3_block_t k = 0; for (lfs3_block_t j = 0;; j++) { assert(j < 2*BLOCK_COUNT); struct lfs3_tinfo tinfo; int err = lfs3_trv_read(&lfs3, &trv, &tinfo); assert(!err || err == LFS3_ERR_NOENT); if (err == LFS3_ERR_NOENT) { lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto done; } // this gets a bit tricky be cause we need to clobber both // blocks in mdir pairs if (tinfo.btype == LFS3_BTYPE_MDIR || tinfo.btype == LFS3_BTYPE_BTREE || tinfo.btype == LFS3_BTYPE_DATA) { if (k == i || k == i+1) { // clobber this block printf("clobbering 0x%x\n", tinfo.block); uint8_t clobber_buf[BLOCK_SIZE]; memset(clobber_buf, 0xcc, BLOCK_SIZE); CFG->erase(CFG, tinfo.block) => 0; CFG->prog(CFG, tinfo.block, 0, clobber_buf, BLOCK_SIZE) => 0; if (tinfo.btype != LFS3_BTYPE_MDIR || k == i+1) { i += (tinfo.btype == LFS3_BTYPE_MDIR) ? 2 : 1; lfs3_trv_close(&lfs3, &trv) => 0; lfs3_unmount(&lfs3) => 0; goto clobbered; } } k += 1; } } clobbered:; // mount with LFS3_M_CKDATA, we should detect clobbered blocks // // note LFS3_M_CKDATA implies LFS3_M_CKMETA lfs3_mount(&lfs3, LFS3_M_RDWR | LFS3_M_CKDATA, CFG) => LFS3_ERR_CORRUPT; } done:; ''' ## incompatiblity tests ## # test that we fail if we find no magic [cases.test_mount_incompat_no_magic] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // delete the magic string // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(1, LFS3_tag_RM | LFS3_TAG_MAGIC, 0), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT; ''' # test that we fail if we find bad magic [cases.test_mount_incompat_bad_magic] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // tweak the magic string // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_MAGIC, 0, LFS3_FROM_BUF, 8), LFS3_RATTR_ARG("lottlefs"), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT; ''' # test that we fail to mount after a major version bump [cases.test_mount_incompat_major] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // bump the major version // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_VERSION, 0, LFS3_FROM_BUF, 2), LFS3_RATTR_ARG(((const uint8_t[2]){ LFS3_DISK_VERSION_MAJOR+1, 0})), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount after a minor version bump [cases.test_mount_incompat_minor] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // bump the minor version // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_VERSION, 0, LFS3_FROM_BUF, 2), LFS3_RATTR_ARG(((const uint8_t[2]){ LFS3_DISK_VERSION_MAJOR, LFS3_DISK_VERSION_MINOR+1})), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount incompatible rcompat flags [cases.test_mount_incompat_rcompat] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set the nonstandard rcompat flag, this will always be incompatible // with standard littlefs // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_LE32), LFS3_RATTR_ARG( lfs3_rcompat(&lfs3) | LFS3_RCOMPAT_NONSTANDARD), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount incompatible wcompat flags [cases.test_mount_incompat_wcompat] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set the nonstandard rcompat flag, this will always be incompatible // with standard littlefs // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_LE32), LFS3_RATTR_ARG( lfs3_wcompat(&lfs3) | LFS3_WCOMPAT_NONSTANDARD), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; // but we _can_ mount readonly lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that an incompatible ocompat flag is a noop [cases.test_mount_incompat_ocompat] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set the nonstandard ocompat flag, this will always be incompatible // with standard littlefs // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_LE32), LFS3_RATTR_ARG( lfs3_ocompat(&lfs3) | LFS3_OCOMPAT_NONSTANDARD), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should _not_ fail, ocompat should always be ignored lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that we fail to mount rdonly images [cases.test_mount_incompat_rdonly] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set the rdonly flag, this prevents writing from a littlefs image // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_LE32), LFS3_RATTR_ARG( lfs3_wcompat(&lfs3) | LFS3_WCOMPAT_RDONLY), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; // but we _can_ mount readonly lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that we fail to mount wronly images [cases.test_mount_incompat_wronly] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set the wronly flag, this prevents reading from a littlefs image // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_LE32), LFS3_RATTR_ARG( lfs3_rcompat(&lfs3) | LFS3_RCOMPAT_WRONLY), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # these are just a bit harder to detect [cases.test_mount_incompat_rcompat_overflow] defines.OVERFLOW = 72 defines.FLAG = 'range(72)' in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set a really far rcompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t rcompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(rcompat_buf, 0, sizeof(rcompat_buf)); lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf); rcompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(rcompat_buf)), LFS3_RATTR_ARG(rcompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' [cases.test_mount_incompat_wcompat_overflow] defines.OVERFLOW = 72 defines.FLAG = 'range(72)' in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; uint8_t flags[9] = {0}; flags[FLAG / 8] |= 1 << (FLAG % 8); // set a really far wcompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t wcompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(wcompat_buf, 0, sizeof(wcompat_buf)); lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf); wcompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(wcompat_buf)), LFS3_RATTR_ARG(wcompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; // but we _can_ mount readonly lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_ocompat_overflow] defines.OVERFLOW = 72 defines.FLAG = 'range(72)' in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set a really far ocompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t ocompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(ocompat_buf, 0, sizeof(ocompat_buf)); ocompat_buf[sizeof(uint32_t) + FLAG/8] |= 1 << (FLAG%8); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(ocompat_buf)), LFS3_RATTR_ARG(ocompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should _not_ fail, ocompat should always be ignored lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # but just appending zeros is _not_ an error [cases.test_mount_incompat_rcompat_padding] defines.OVERFLOW = 72 in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set a really far rcompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t rcompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(rcompat_buf, 0, sizeof(rcompat_buf)); lfs3_data_fromle32(lfs3_rcompat(&lfs3), rcompat_buf); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_RCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(rcompat_buf)), LFS3_RATTR_ARG(rcompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should _not_ fail, extra zeros should be ignored lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_wcompat_padding] defines.OVERFLOW = 72 in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; uint8_t flags[9] = {0}; flags[FLAG / 8] |= 1 << (FLAG % 8); // set a really far wcompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t wcompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(wcompat_buf, 0, sizeof(wcompat_buf)); lfs3_data_fromle32(lfs3_wcompat(&lfs3), wcompat_buf); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_WCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(wcompat_buf)), LFS3_RATTR_ARG(wcompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should _not_ fail, extra zeros should be ignored lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_ocompat_padding] defines.OVERFLOW = 72 in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set a really far ocompat flag // // note we're messing around with internals to do this! this // is not a user API uint8_t ocompat_buf[sizeof(uint32_t) + OVERFLOW/8]; memset(ocompat_buf, 0, sizeof(ocompat_buf)); lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_OCOMPAT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(sizeof(ocompat_buf)), LFS3_RATTR_ARG(ocompat_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should _not_ fail, extra zeros should be ignored lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_unmount(&lfs3) => 0; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => 0; lfs3_unmount(&lfs3) => 0; ''' # test that we fail to mount incompatible block sizes [cases.test_mount_incompat_block_size] defines.INC_BLOCK_SIZE = ['BLOCK_SIZE/2', 'BLOCK_SIZE*2'] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set an incompatible block size // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_GEOMETRY, 0, LFS3_FROM_GEOMETRY), LFS3_RATTR_ARG((&(lfs3_geometry_t){ INC_BLOCK_SIZE, BLOCK_COUNT})), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount after incompatible block counts [cases.test_mount_incompat_block_count] defines.INC_BLOCK_COUNT = ['BLOCK_COUNT*2'] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set an incompatible block count // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_GEOMETRY, 0, LFS3_FROM_GEOMETRY), LFS3_RATTR_ARG((&(lfs3_geometry_t){ BLOCK_SIZE, INC_BLOCK_COUNT})), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount after incompatible name limit [cases.test_mount_incompat_name_limit] defines.INC_NAME_LIMIT = ['LFS3_NAME_MAX*2'] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set an incompatible block size // // note we're messing around with internals to do this! this // is not a user API lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(2, LFS3_TAG_NAMELIMIT, 0, LFS3_FROM_LEB128), LFS3_RATTR_ARG(INC_NAME_LIMIT), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test that we fail to mount after incompatible file limit [cases.test_mount_incompat_file_limit] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // set an incompatible file limit // // note we're messing around with internals to do this! this // is not a user API // it's a bit difficult to test this since file limit // is usually our integer limit, but we can force a // larger value by inserting an extra byte into our // leb128 encoding uint8_t filelimit_buf[1+LFS3_LEB128_DSIZE]; filelimit_buf[0] = 0xff; lfs3_ssize_t d_ = lfs3_toleb128(LFS3_FILE_MAX, filelimit_buf+1, LFS3_LEB128_DSIZE); assert(d_ >= 0); lfs3_size_t d = 1 + d_; lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_FILELIMIT, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(d), LFS3_RATTR_ARG(filelimit_buf), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test what happens if we find an unknown config [cases.test_mount_incompat_unknown_config] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create an unknown config lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_mdir_commit(&lfs3, &lfs3.mroot, LFS3_RATTRS( LFS3_RATTR(3, LFS3_TAG_CONFIG + 0x42, 0, LFS3_FROM_DATA), LFS3_RATTR_ARG(strlen("oh no!")), LFS3_RATTR_ARG("oh no!"), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_NOTSUP; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_NOTSUP; ''' # test what happens if we find an unknown file type [cases.test_mount_incompat_unknown_type] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi c!", strlen("hi c!")) => strlen("hi c!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // open/mkdir should error lfs3_file_open(&lfs3, &file, "b", LFS3_O_RDONLY) => LFS3_ERR_NOTSUP; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT) => LFS3_ERR_NOTSUP; lfs3_mkdir(&lfs3, "b") => LFS3_ERR_EXIST; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_unknown_type_rm] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi c!", strlen("hi c!")) => strlen("hi c!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // removing unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_remove(&lfs3, "b") => 0; // check that things look reasonable lfs3_stat(&lfs3, "b", &info) => LFS3_ERR_NOENT; 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_unknown_type_mv_src] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi c!", strlen("hi c!")) => strlen("hi c!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "b", "c") => 0; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "c", &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); 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_mount_incompat_unknown_type_mv_dst] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi c!", strlen("hi c!")) => strlen("hi c!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "c", "b") => 0; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_unknown_type_mv_src_dst] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh hi!", strlen("oh hi!")) => strlen("oh hi!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; path = "c"; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "b", "c") => 0; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "c", &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); 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_mount_incompat_unknown_type_mv_noop] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi c!", strlen("hi c!")) => strlen("hi c!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "b", "b") => 0; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi c!")); lfs3_dir_read(&lfs3, &dir, &info) => LFS3_ERR_NOENT; lfs3_dir_close(&lfs3, &dir) => 0; lfs3_unmount(&lfs3) => 0; ''' [cases.test_mount_incompat_unknown_type_mv_notdir] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_mkdir(&lfs3, "c") => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 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; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "c", "b") => LFS3_ERR_NOTDIR; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 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_mount_incompat_unknown_type_mv_isdir] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // create some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "a", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "hi a!", strlen("hi a!")) => strlen("hi a!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "b", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; lfs3_file_write(&lfs3, &file, "oh no!", strlen("oh no!")) => strlen("oh no!"); lfs3_file_close(&lfs3, &file) => 0; lfs3_mkdir(&lfs3, "c") => 0; lfs3_unmount(&lfs3) => 0; // change a file's type to something unknown lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; const char *path = "b"; lfs3_mdir_t mdir; lfs3_did_t did; lfs3_mtree_pathlookup(&lfs3, &path, &mdir, &did) => LFS3_TAG_REG; lfs3_mdir_commit(&lfs3, &mdir, LFS3_RATTRS( LFS3_RATTR(3, LFS3_tag_MASK8 | (LFS3_TAG_NAME + 0x13), 0, LFS3_FROM_NAME), LFS3_RATTR_ARG(did), LFS3_RATTR_ARG(path), LFS3_RATTR_NULL)) => 0; lfs3_unmount(&lfs3) => 0; // mount lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; // our file should appear as an unknown type struct lfs3_info info; lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 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; // renaming unknown files should still work, if this would leak // resources the new type should set a wcompat flag lfs3_rename(&lfs3, "b", "c") => LFS3_ERR_ISDIR; // check that things look reasonable after renaming/removing lfs3_stat(&lfs3, "b", &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); 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, "a") == 0); assert(info.type == LFS3_TYPE_REG); assert(info.size == strlen("hi a!")); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "b") == 0); assert(info.type == LFS3_TYPE_UNKNOWN); assert(info.size == 0); lfs3_dir_read(&lfs3, &dir, &info) => 0; assert(strcmp(info.name, "c") == 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; ''' # Ok, here's an interesting one, test that we fail if we're # "out-of-phase", i.e. the mrootanchor has been shifted by a small # number of blocks. # # This can happen if we find the wrong mrootanchor (after, say, a magic # scan), and risks filesystem corruption. To prevent this, we include 2 # phase bits in cksum tags to detect up to a 3 block shift (the maximum # number of redund mrootanchors) # [cases.test_mount_incompat_out_of_phase] defines.PHASE = [1, 2, 3, 4] in = 'lfs3.c' code = ''' // create a superblock lfs3_t lfs3; lfs3_format(&lfs3, LFS3_F_RDWR | ((GBMAP) ? LFS3_IFDEF_GBMAP(LFS3_F_GBMAP, -1) : 0), CFG) => 0; // with some files lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0; lfs3_file_t file; lfs3_file_open(&lfs3, &file, "r2d2", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; char wbuf[256]; strcpy(wbuf, "beep boop"); lfs3_file_write(&lfs3, &file, wbuf, strlen(wbuf)) => strlen(wbuf); lfs3_file_close(&lfs3, &file) => 0; lfs3_file_open(&lfs3, &file, "c3po", LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0; strcpy(wbuf, "we seem to be made to suffer"); lfs3_file_write(&lfs3, &file, wbuf, strlen(wbuf)) => strlen(wbuf); lfs3_file_close(&lfs3, &file) => 0; // shift the filesystem out-of-phase uint8_t shift_buf[BLOCK_SIZE]; for (lfs3_size_t i = 0; i < 4; i++) { CFG->read(CFG, 4-1-i, 0, shift_buf, BLOCK_SIZE) => 0; CFG->erase(CFG, 4-1-i + PHASE) => 0; CFG->prog(CFG, 4-1-i + PHASE, 0, shift_buf, BLOCK_SIZE) => 0; memset(shift_buf, 0, BLOCK_SIZE); strcpy((char*)shift_buf, "these aren't the files you're looking for ;)"); CFG->erase(CFG, 4-1-i) => 0; CFG->prog(CFG, 4-1-i, 0, shift_buf, BLOCK_SIZE) => 0; } // mount should now fail lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => LFS3_ERR_CORRUPT; lfs3_mount(&lfs3, LFS3_M_RDONLY, CFG) => LFS3_ERR_CORRUPT; '''