# Tests covering properties of the block allocator after = 'test_mtree' # TODO test all of these with weird block sizes? would be nice to make this # easy via the test_runner, either by handling it there or letting a single # config limit the block count by a couple blocks # test that we can alloc [cases.test_alloc_blocks] in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // start allocating lfs_alloc_ack(&lfs); lfs_size_t alloced = 0; while (true) { lfs_block_t block; int err = lfs_alloc(&lfs, &block); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { break; } alloced += 1; // our allocator should stop at some point... assert(alloced < 2*BLOCK_COUNT); } // excluding our mroot, we should have allocated exactly // block_count-2 blocks printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT); assert(alloced == BLOCK_COUNT-2); lfsr_unmount(&lfs) => 0; ''' # test that we can realloc after an ack [cases.test_alloc_reuse] in = 'lfs.c' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // start allocating lfs_alloc_ack(&lfs); lfs_size_t alloced = 0; while (true) { lfs_block_t block; int err = lfs_alloc(&lfs, &block); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { break; } alloced += 1; // our allocator should stop at some point... assert(alloced < 2*BLOCK_COUNT); } // excluding our mroot, we should have allocated exactly // block_count-2 blocks printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT); assert(alloced == BLOCK_COUNT-2); // ack again, effectively releasing all the previously alloced blocks lfs_alloc_ack(&lfs); alloced = 0; while (true) { lfs_block_t block; int err = lfs_alloc(&lfs, &block); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { break; } alloced += 1; // our allocator should stop at some point... assert(alloced < 2*BLOCK_COUNT); } // excluding our mroot, we should have allocated exactly // block_count-2 blocks printf("alloced %d/%d blocks\n", alloced, (lfs_block_t)BLOCK_COUNT); assert(alloced == BLOCK_COUNT-2); lfsr_unmount(&lfs) => 0; ''' # test that we can alloc an mtree, the difference between this and mtree tests # is we expect this to be able to handle wrap-around [cases.test_alloc_mtree] in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; lfs_alloc_ack(&lfs); // remove root dstart for now lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( LFSR_ATTR(0, UNR, -1, NULL))) => 0; lfsr_mdir_t mdir; lfsr_mtree_lookup(&lfs, LFSR_MID(lfsr_mtree_weight(&lfs)-1, -1), &mdir) => 0; mdir.mid.rid = 0; lfs_size_t count = 0; while (true) { // at least try to catch infinite loops assert(count < BLOCK_SIZE * BLOCK_COUNT/2); // ack before each commit to reset the allocator lfs_alloc_ack(&lfs); // keep creating new metadata entries until we run out of space int err = lfsr_mdir_commit(&lfs, &mdir, LFSR_ATTRS( LFSR_ATTR(mdir.mid.rid, INLINED, +1, BUF(&alphas[count % 26], 1)))); assert(!err || err == LFS_ERR_NOSPC); if (err == LFS_ERR_NOSPC) { break; } uint8_t buffer[4]; lfsr_mdir_get(&lfs, &mdir, mdir.mid.rid, LFSR_TAG_INLINED, buffer, 4) => 1; assert(memcmp(buffer, &alphas[count % 26], 1) == 0); count += 1; mdir.mid.rid += 1; } printf("alloced %d metadata entries in %d blocks\n", count, (lfs_block_t)BLOCK_COUNT); // test that all of our metadata entries are still there lfs_size_t i = 0; for (lfs_ssize_t mid = (lfsr_mtree_isinlined(&lfs) ? -1 : 0); mid < (lfs_ssize_t)lfsr_mtree_weight(&lfs); mid++) { lfsr_mdir_t mdir; lfsr_mtree_lookup(&lfs, LFSR_MID(mid, -1), &mdir) => 0; for (mdir.mid.rid = 0; mdir.mid.rid < (lfs_ssize_t)mdir.u.m.weight; mdir.mid.rid++) { uint8_t buffer[4]; lfsr_mdir_get(&lfs, &mdir, mdir.mid.rid, LFSR_TAG_INLINED, buffer, 4) => 1; assert(memcmp(buffer, &alphas[i % 26], 1) == 0); i += 1; } } assert(i == count); lfsr_unmount(&lfs) => 0; ''' ## allocator tests ## note for these to work there are a number constraints on the device geometry #if = 'BLOCK_CYCLES == -1' # ## parallel allocation test #[cases.test_alloc_parallel] #defines.FILES = 3 #defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' #code = ''' # const char *names[] = {"bacon", "eggs", "pancakes"}; # lfs_file_t files[FILES]; # # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_open(&lfs, &files[n], path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # } # for (int n = 0; n < FILES; n++) { # size_t size = strlen(names[n]); # for (lfs_size_t i = 0; i < SIZE; i += size) { # lfs_file_write(&lfs, &files[n], names[n], size) => size; # } # } # for (int n = 0; n < FILES; n++) { # lfs_file_close(&lfs, &files[n]) => 0; # } # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # size_t size = strlen(names[n]); # for (lfs_size_t i = 0; i < SIZE; i += size) { # uint8_t buffer[1024]; # lfs_file_read(&lfs, &file, buffer, size) => size; # assert(memcmp(buffer, names[n], size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; #''' # ## serial allocation test #[cases.test_alloc_serial] #defines.FILES = 3 #defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' #code = ''' # const char *names[] = {"bacon", "eggs", "pancakes"}; # # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # # for (int n = 0; n < FILES; n++) { # lfs_mount(&lfs, cfg) => 0; # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # size_t size = strlen(names[n]); # uint8_t buffer[1024]; # memcpy(buffer, names[n], size); # for (int i = 0; i < SIZE; i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # } # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # size_t size = strlen(names[n]); # for (int i = 0; i < SIZE; i += size) { # uint8_t buffer[1024]; # lfs_file_read(&lfs, &file, buffer, size) => size; # assert(memcmp(buffer, names[n], size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; #''' # ## parallel allocation reuse test #[cases.test_alloc_parallel_reuse] #defines.FILES = 3 #defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' #defines.CYCLES = [1, 10] #code = ''' # const char *names[] = {"bacon", "eggs", "pancakes"}; # lfs_file_t files[FILES]; # # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # for (int c = 0; c < CYCLES; c++) { # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_open(&lfs, &files[n], path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # } # for (int n = 0; n < FILES; n++) { # size_t size = strlen(names[n]); # for (int i = 0; i < SIZE; i += size) { # lfs_file_write(&lfs, &files[n], names[n], size) => size; # } # } # for (int n = 0; n < FILES; n++) { # lfs_file_close(&lfs, &files[n]) => 0; # } # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # size_t size = strlen(names[n]); # for (int i = 0; i < SIZE; i += size) { # uint8_t buffer[1024]; # lfs_file_read(&lfs, &file, buffer, size) => size; # assert(memcmp(buffer, names[n], size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_remove(&lfs, path) => 0; # } # lfs_remove(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # } #''' # ## serial allocation reuse test #[cases.test_alloc_serial_reuse] #defines.FILES = 3 #defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)' #defines.CYCLES = [1, 10] #code = ''' # const char *names[] = {"bacon", "eggs", "pancakes"}; # # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # # for (int c = 0; c < CYCLES; c++) { # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # # for (int n = 0; n < FILES; n++) { # lfs_mount(&lfs, cfg) => 0; # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, # LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; # size_t size = strlen(names[n]); # uint8_t buffer[1024]; # memcpy(buffer, names[n], size); # for (int i = 0; i < SIZE; i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # } # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_file_t file; # lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; # size_t size = strlen(names[n]); # for (int i = 0; i < SIZE; i += size) { # uint8_t buffer[1024]; # lfs_file_read(&lfs, &file, buffer, size) => size; # assert(memcmp(buffer, names[n], size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # } # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # for (int n = 0; n < FILES; n++) { # char path[1024]; # sprintf(path, "breakfast/%s", names[n]); # lfs_remove(&lfs, path) => 0; # } # lfs_remove(&lfs, "breakfast") => 0; # lfs_unmount(&lfs) => 0; # } #''' # ## exhaustion test #[cases.test_alloc_exhaustion] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # size_t size = strlen("exhaustion"); # uint8_t buffer[1024]; # memcpy(buffer, "exhaustion", size); # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # lfs_ssize_t res; # while (true) { # res = lfs_file_write(&lfs, &file, buffer, size); # if (res < 0) { # break; # } # # res => size; # } # res => LFS_ERR_NOSPC; # # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); # size = strlen("exhaustion"); # lfs_file_size(&lfs, &file) => size; # lfs_file_read(&lfs, &file, buffer, size) => size; # memcmp(buffer, "exhaustion", size) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # ## exhaustion wraparound test #[cases.test_alloc_exhaustion_wraparound] #defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-4)) / 3)' #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # lfs_file_t file; # lfs_file_open(&lfs, &file, "padding", LFS_O_WRONLY | LFS_O_CREAT); # size_t size = strlen("buffering"); # uint8_t buffer[1024]; # memcpy(buffer, "buffering", size); # for (int i = 0; i < SIZE; i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # lfs_remove(&lfs, "padding") => 0; # # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # size = strlen("exhaustion"); # memcpy(buffer, "exhaustion", size); # lfs_file_write(&lfs, &file, buffer, size) => size; # lfs_file_sync(&lfs, &file) => 0; # # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # lfs_ssize_t res; # while (true) { # res = lfs_file_write(&lfs, &file, buffer, size); # if (res < 0) { # break; # } # # res => size; # } # res => LFS_ERR_NOSPC; # # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_RDONLY); # size = strlen("exhaustion"); # lfs_file_size(&lfs, &file) => size; # lfs_file_read(&lfs, &file, buffer, size) => size; # memcmp(buffer, "exhaustion", size) => 0; # lfs_file_close(&lfs, &file) => 0; # lfs_remove(&lfs, "exhaustion") => 0; # lfs_unmount(&lfs) => 0; #''' # ## dir exhaustion test #[cases.test_alloc_dir_exhaustion] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # // find out max file size # lfs_mkdir(&lfs, "exhaustiondir") => 0; # size_t size = strlen("blahblahblahblah"); # uint8_t buffer[1024]; # memcpy(buffer, "blahblahblahblah", size); # lfs_file_t file; # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # int count = 0; # int err; # while (true) { # err = lfs_file_write(&lfs, &file, buffer, size); # if (err < 0) { # break; # } # # count += 1; # } # err => LFS_ERR_NOSPC; # lfs_file_close(&lfs, &file) => 0; # # lfs_remove(&lfs, "exhaustion") => 0; # lfs_remove(&lfs, "exhaustiondir") => 0; # # // see if dir fits with max file size # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # for (int i = 0; i < count; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_mkdir(&lfs, "exhaustiondir") => 0; # lfs_remove(&lfs, "exhaustiondir") => 0; # lfs_remove(&lfs, "exhaustion") => 0; # # // see if dir fits with > max file size # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # for (int i = 0; i < count+1; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; # # lfs_remove(&lfs, "exhaustion") => 0; # lfs_unmount(&lfs) => 0; #''' # ## what if we have a bad block during an allocation scan? #[cases.test_alloc_bad_blocks] #in = "lfs.c" #defines.ERASE_CYCLES = 0xffffffff #defines.BADBLOCK_BEHAVIOR = 'LFS_EMUBD_BADBLOCK_READERROR' #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # // first fill to exhaustion to find available space # lfs_file_t file; # lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; # uint8_t buffer[1024]; # strcpy((char*)buffer, "waka"); # size_t size = strlen("waka"); # lfs_size_t filesize = 0; # while (true) { # lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); # assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); # if (res == LFS_ERR_NOSPC) { # break; # } # filesize += size; # } # lfs_file_close(&lfs, &file) => 0; # // now fill all but a couple of blocks of the filesystem with data # filesize -= 3*BLOCK_SIZE; # lfs_file_open(&lfs, &file, "pacman", LFS_O_WRONLY | LFS_O_CREAT) => 0; # strcpy((char*)buffer, "waka"); # size = strlen("waka"); # for (lfs_size_t i = 0; i < filesize/size; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # // also save head of file so we can error during lookahead scan # lfs_block_t fileblock = file.ctz.head; # lfs_unmount(&lfs) => 0; # # // remount to force an alloc scan # lfs_mount(&lfs, cfg) => 0; # # // but mark the head of our file as a "bad block", this is force our # // scan to bail early # lfs_emubd_setwear(cfg, fileblock, 0xffffffff) => 0; # lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; # strcpy((char*)buffer, "chomp"); # size = strlen("chomp"); # while (true) { # lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); # assert(res == (lfs_ssize_t)size || res == LFS_ERR_CORRUPT); # if (res == LFS_ERR_CORRUPT) { # break; # } # } # lfs_file_close(&lfs, &file) => 0; # # // now reverse the "bad block" and try to write the file again until we # // run out of space # lfs_emubd_setwear(cfg, fileblock, 0) => 0; # lfs_file_open(&lfs, &file, "ghost", LFS_O_WRONLY | LFS_O_CREAT) => 0; # strcpy((char*)buffer, "chomp"); # size = strlen("chomp"); # while (true) { # lfs_ssize_t res = lfs_file_write(&lfs, &file, buffer, size); # assert(res == (lfs_ssize_t)size || res == LFS_ERR_NOSPC); # if (res == LFS_ERR_NOSPC) { # break; # } # } # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; # # // check that the disk isn't hurt # lfs_mount(&lfs, cfg) => 0; # lfs_file_open(&lfs, &file, "pacman", LFS_O_RDONLY) => 0; # strcpy((char*)buffer, "waka"); # size = strlen("waka"); # for (lfs_size_t i = 0; i < filesize/size; i++) { # uint8_t rbuffer[4]; # lfs_file_read(&lfs, &file, rbuffer, size) => size; # assert(memcmp(rbuffer, buffer, size) == 0); # } # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # # ## Below, I don't like these tests. They're fragile and depend _heavily_ ## on the geometry of the block device. But they are valuable. Eventually they ## should be removed and replaced with generalized tests. # ## chained dir exhaustion test #[cases.test_alloc_chained_dir_exhaustion] #if = 'BLOCK_SIZE == 512' #defines.BLOCK_COUNT = 1024 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # // find out max file size # lfs_mkdir(&lfs, "exhaustiondir") => 0; # for (int i = 0; i < 10; i++) { # char path[1024]; # sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); # lfs_mkdir(&lfs, path) => 0; # } # size_t size = strlen("blahblahblahblah"); # uint8_t buffer[1024]; # memcpy(buffer, "blahblahblahblah", size); # lfs_file_t file; # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # int count = 0; # int err; # while (true) { # err = lfs_file_write(&lfs, &file, buffer, size); # if (err < 0) { # break; # } # # count += 1; # } # err => LFS_ERR_NOSPC; # lfs_file_close(&lfs, &file) => 0; # # lfs_remove(&lfs, "exhaustion") => 0; # lfs_remove(&lfs, "exhaustiondir") => 0; # for (int i = 0; i < 10; i++) { # char path[1024]; # sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); # lfs_remove(&lfs, path) => 0; # } # # // see that chained dir fails # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # for (int i = 0; i < count+1; i++) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_sync(&lfs, &file) => 0; # # for (int i = 0; i < 10; i++) { # char path[1024]; # sprintf(path, "dirwithanexhaustivelylongnameforpadding%d", i); # lfs_mkdir(&lfs, path) => 0; # } # # lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; # # // shorten file to try a second chained dir # while (true) { # err = lfs_mkdir(&lfs, "exhaustiondir"); # if (err != LFS_ERR_NOSPC) { # break; # } # # lfs_ssize_t filesize = lfs_file_size(&lfs, &file); # filesize > 0 => true; # # lfs_file_truncate(&lfs, &file, filesize - size) => 0; # lfs_file_sync(&lfs, &file) => 0; # } # err => 0; # # lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC; # # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #''' # ## split dir test #[cases.test_alloc_split_dir] #if = 'BLOCK_SIZE == 512' #defines.BLOCK_COUNT = 1024 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # // create one block hole for half a directory # lfs_file_t file; # lfs_file_open(&lfs, &file, "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; # for (lfs_size_t i = 0; i < cfg->block_size; i += 2) { # uint8_t buffer[1024]; # memcpy(&buffer[i], "hi", 2); # } # uint8_t buffer[1024]; # lfs_file_write(&lfs, &file, buffer, cfg->block_size) => cfg->block_size; # lfs_file_close(&lfs, &file) => 0; # # lfs_file_open(&lfs, &file, "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); # size_t size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < (cfg->block_count-4)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # // remount to force reset of lookahead # lfs_unmount(&lfs) => 0; # lfs_mount(&lfs, cfg) => 0; # # // open hole # lfs_remove(&lfs, "bump") => 0; # # lfs_mkdir(&lfs, "splitdir") => 0; # lfs_file_open(&lfs, &file, "splitdir/bump", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # for (lfs_size_t i = 0; i < cfg->block_size; i += 2) { # memcpy(&buffer[i], "hi", 2); # } # lfs_file_write(&lfs, &file, buffer, 2*cfg->block_size) => LFS_ERR_NOSPC; # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; #''' # ## outdated lookahead test #[cases.test_alloc_outdated_lookahead] #if = 'BLOCK_SIZE == 512' #defines.BLOCK_COUNT = 1024 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # // fill completely with two files # lfs_file_t file; # lfs_file_open(&lfs, &file, "exhaustion1", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # size_t size = strlen("blahblahblahblah"); # uint8_t buffer[1024]; # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_file_open(&lfs, &file, "exhaustion2", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # // remount to force reset of lookahead # lfs_unmount(&lfs) => 0; # lfs_mount(&lfs, cfg) => 0; # # // rewrite one file # lfs_file_open(&lfs, &file, "exhaustion1", # LFS_O_WRONLY | LFS_O_TRUNC) => 0; # lfs_file_sync(&lfs, &file) => 0; # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # // rewrite second file, this requires lookahead does not # // use old population # lfs_file_open(&lfs, &file, "exhaustion2", # LFS_O_WRONLY | LFS_O_TRUNC) => 0; # lfs_file_sync(&lfs, &file) => 0; # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; #''' # ## outdated lookahead and split dir test #[cases.test_alloc_outdated_lookahead_split_dir] #if = 'BLOCK_SIZE == 512' #defines.BLOCK_COUNT = 1024 #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # # // fill completely with two files # lfs_file_t file; # lfs_file_open(&lfs, &file, "exhaustion1", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # size_t size = strlen("blahblahblahblah"); # uint8_t buffer[1024]; # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # lfs_file_open(&lfs, &file, "exhaustion2", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2+1)/2)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # // remount to force reset of lookahead # lfs_unmount(&lfs) => 0; # lfs_mount(&lfs, cfg) => 0; # # // rewrite one file with a hole of one block # lfs_file_open(&lfs, &file, "exhaustion1", # LFS_O_WRONLY | LFS_O_TRUNC) => 0; # lfs_file_sync(&lfs, &file) => 0; # size = strlen("blahblahblahblah"); # memcpy(buffer, "blahblahblahblah", size); # for (lfs_size_t i = 0; # i < ((cfg->block_count-2)/2 - 1)*(cfg->block_size-8); # i += size) { # lfs_file_write(&lfs, &file, buffer, size) => size; # } # lfs_file_close(&lfs, &file) => 0; # # // try to allocate a directory, should fail! # lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC; # # // file should not fail # lfs_file_open(&lfs, &file, "notasplit", # LFS_O_WRONLY | LFS_O_CREAT) => 0; # lfs_file_write(&lfs, &file, "hi", 2) => 2; # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; #'''