# Custom attribute tests after = ['test_files', 'test_fsync', 'test_forphans'] ## General setattr/getattr tests # test some simple attr operations [cases.test_attrs_setattr] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => strlen(a); lfsr_sizeattr(&lfs, path, 'b') => strlen(b); lfsr_sizeattr(&lfs, path, 'c') => strlen(c); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test truncated getattr calls still work [cases.test_attrs_setattr_trunc] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] defines.BUFSIZE = [1, 4, 7] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => strlen(a); lfsr_sizeattr(&lfs, path, 'b') => strlen(b); lfsr_sizeattr(&lfs, path, 'c') => strlen(c); // mark rbuf so we can detect overflow uint8_t rbuf[256]; rbuf[BUFSIZE] = '!'; // try reading truncated attrs lfsr_getattr(&lfs, path, 'a', rbuf, BUFSIZE) => BUFSIZE; assert(memcmp(rbuf, a, BUFSIZE) == 0); assert(rbuf[BUFSIZE] == '!'); lfsr_getattr(&lfs, path, 'b', rbuf, BUFSIZE) => BUFSIZE; assert(memcmp(rbuf, b, BUFSIZE) == 0); assert(rbuf[BUFSIZE] == '!'); lfsr_getattr(&lfs, path, 'c', rbuf, BUFSIZE) => BUFSIZE; assert(memcmp(rbuf, c, BUFSIZE) == 0); assert(rbuf[BUFSIZE] == '!'); // try reading the full attrs lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test ENOATTR works [cases.test_attrs_setattr_noattr] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR; // try reading attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // make sure setattr didn't quietly create attrs // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR; // try reading attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } lfsr_unmount(&lfs) => 0; ''' # test that updating attrs works [cases.test_attrs_setattr_update] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] # update based on this mask defines.MASK = 'range(0x8)' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; // rewrite some attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; if (MASK & 0x1) { lfsr_setattr(&lfs, path, 'a', a_, strlen(a_)) => 0; } const char *b_ = "Three slash four cup butter or margarine."; if (MASK & 0x2) { lfsr_setattr(&lfs, path, 'b', b_, strlen(b_)) => 0; } const char *c_ = "One and two third cups granulated sugar."; if (MASK & 0x4) { lfsr_setattr(&lfs, path, 'c', c_, strlen(c_)) => 0; } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes if (MASK & 0x1) { lfsr_sizeattr(&lfs, path, 'a') => strlen(a_); } else { lfsr_sizeattr(&lfs, path, 'a') => strlen(a); } if (MASK & 0x2) { lfsr_sizeattr(&lfs, path, 'b') => strlen(b_); } else { lfsr_sizeattr(&lfs, path, 'b') => strlen(b); } if (MASK & 0x4) { lfsr_sizeattr(&lfs, path, 'c') => strlen(c_); } else { lfsr_sizeattr(&lfs, path, 'c') => strlen(c); } // try reading attrs uint8_t rbuf[256]; if (MASK & 0x1) { lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a_); assert(memcmp(rbuf, a_, strlen(a_)) == 0); } else { lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); } if (MASK & 0x2) { lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b_); assert(memcmp(rbuf, b_, strlen(b_)) == 0); } else { lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); } if (MASK & 0x4) { lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c_); assert(memcmp(rbuf, c_, strlen(c_)) == 0); } else { lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } } lfsr_unmount(&lfs) => 0; ''' # test removing attrs [cases.test_attrs_removeattr] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] # remove based on this mask defines.MASK = 'range(0x8)' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; // remove some attrs if (MASK & 0x1) { lfsr_removeattr(&lfs, path, 'a') => 0; } if (MASK & 0x2) { lfsr_removeattr(&lfs, path, 'b') => 0; } if (MASK & 0x4) { lfsr_removeattr(&lfs, path, 'c') => 0; } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes if (MASK & 0x1) { lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR; } else { lfsr_sizeattr(&lfs, path, 'a') => strlen(a); } if (MASK & 0x2) { lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR; } else { lfsr_sizeattr(&lfs, path, 'b') => strlen(b); } if (MASK & 0x4) { lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR; } else { lfsr_sizeattr(&lfs, path, 'c') => strlen(c); } // try reading the full attrs uint8_t rbuf[256]; if (MASK & 0x1) { lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } else { lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); } if (MASK & 0x2) { lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } else { lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); } if (MASK & 0x4) { lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } else { lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } } lfsr_unmount(&lfs) => 0; ''' # test the full range of attrs [cases.test_attrs_all] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] defines.SIZE = 4 code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // try creating every attr, this tests any encoding quirks uint32_t prng = 42; for (uint16_t a = 0; a < 0x100; a++) { // create the attr uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; // try getting the attr size lfsr_sizeattr(&lfs, path, a) => SIZE; // try reading the attr uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // remove the attr lfsr_removeattr(&lfs, path, a) => 0; } lfsr_unmount(&lfs) => 0; ''' # test creating a bunch of attrs [cases.test_attrs_many] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] defines.M = 40 defines.SIZE = 4 defines.COMPACT = [false, true] defines.GC_FLAGS = 'LFS_GC_COMPACT' defines.GC_STEPS = -1 defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' if = 'LFS_IFDEF_GC(true, !COMPACT)' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // create M attrs uint32_t prng = 42; for (uint16_t a = 0; a < M; a++) { uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; } // try compacting? #ifdef LFS_GC if (COMPACT) { lfsr_fs_gc(&lfs) => 0; } #endif for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes for (uint16_t a = 0; a < M; a++) { lfsr_sizeattr(&lfs, path, a) => SIZE; } // try reading the attrs prng = 42; for (uint16_t a = 0; a < M; a++) { uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } lfsr_unmount(&lfs) => 0; ''' # test creating a bunch of attrs on a bunch of files [cases.test_attrs_many_many] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.COMPACT = [false, true] defines.GC_FLAGS = 'LFS_GC_COMPACT' defines.GC_STEPS = -1 defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' if = 'LFS_IFDEF_GC(true, !COMPACT)' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create N files uint32_t prng = 42; for (lfs_size_t i = 0; i < N; i++) { char path[256]; // create a file? if (FILETYPE == 0) { sprintf(path, "cat%03x", i); lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { sprintf(path, "armadillo%03x", i); lfsr_mkdir(&lfs, path) => 0; } // create M attrs for (uint16_t a = 0; a < M; a++) { uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; } } // try compacting? #ifdef LFS_GC if (COMPACT) { lfsr_fs_gc(&lfs) => 0; } #endif for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } prng = 42; for (lfs_size_t x = 0; x < N; x++) { char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // try getting the attr sizes for (uint16_t a = 0; a < M; a++) { lfsr_sizeattr(&lfs, path, a) => SIZE; } // try reading the attrs for (uint16_t a = 0; a < M; a++) { uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } } lfsr_unmount(&lfs) => 0; ''' # fuzz attrs [cases.test_attrs_fuzz] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory # FILETYPE=2 => root defines.FILETYPE = [0, 1, 2] defines.M = 10 defines.SIZE = 4 defines.OPS = '4*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else if (FILETYPE == 1) { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; // do nothing for root } else { path = "/"; } // set up a simulation to compare against uint32_t *sim_prngs = malloc(M*sizeof(uint32_t)); memset(sim_prngs, 0, M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 2; // create an attr? if (op == 0) { // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // create the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; // update our sim sim_prngs[a] = wprng; // remove an attr? } else if (op == 1) { // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr if (sim_prngs[a]) { lfsr_removeattr(&lfs, path, a) => 0; } else { lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR; } // update our sim sim_prngs[a] = 0; } } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting each attr size for (uint16_t a = 0; a < M; a++) { if (sim_prngs[a]) { lfsr_sizeattr(&lfs, path, a) => SIZE; } else { lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR; } } // try reading each attr for (uint16_t a = 0; a < M; a++) { if (sim_prngs[a]) { uint32_t wprng_ = sim_prngs[a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } else { uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } } } // clean up sim/lfs free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # fuzz attrs on multiple files [cases.test_attrs_fuzz_fuzz] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create N files for (lfs_size_t i = 0; i < N; i++) { char path[256]; // create a file? if (FILETYPE == 0) { sprintf(path, "cat%03x", i); lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { sprintf(path, "armadillo%03x", i); lfsr_mkdir(&lfs, path) => 0; } } // set up a simulation to compare against uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t)); memset(sim_prngs, 0, N*M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 2; // create an attr? if (op == 0) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // create the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; // update our sim sim_prngs[x*M+a] = wprng; // remove an attr? } else if (op == 1) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr if (sim_prngs[x*M+a]) { lfsr_removeattr(&lfs, path, a) => 0; } else { lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR; } // update our sim sim_prngs[x*M+a] = 0; } } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } for (lfs_size_t x = 0; x < N; x++) { char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // try getting each attr size for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { lfsr_sizeattr(&lfs, path, a) => SIZE; } else { lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR; } } // try reading each attr for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } else { uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } } } } // clean up sim/lfs free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # test that removing a file removes all attrs [cases.test_attrs_rm] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; // remove the file lfsr_remove(&lfs, path) => 0; // create the file again // file? if (FILETYPE == 0) { lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; // dir? } else { lfsr_mkdir(&lfs, path) => 0; } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR; // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } lfsr_unmount(&lfs) => 0; ''' # test that moving onto a file removes all attrs [cases.test_attrs_mv_dst] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // create a file? if (FILETYPE == 0) { path = "cat"; lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { path = "armadillo"; lfsr_mkdir(&lfs, path) => 0; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, path, 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, path, 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, path, 'c', c, strlen(c)) => 0; // create another file and move it onto our file // file? if (FILETYPE == 0) { lfsr_file_t file; lfsr_file_open(&lfs, &file, "beaver", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; // dir? } else { lfsr_mkdir(&lfs, "beaver") => 0; } lfsr_rename(&lfs, "beaver", path) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR; // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } lfsr_unmount(&lfs) => 0; ''' # test that moves bring over all attrs [cases.test_attrs_mv_src] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] defines.REPLACE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; const char *path; // file? if (FILETYPE == 0) { path = "cat"; // dir? } else { path = "armadillo"; } // if replacing create a file to replace if (REPLACE) { // create a file? if (FILETYPE == 0) { lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { lfsr_mkdir(&lfs, path) => 0; } } // create a file? if (FILETYPE == 0) { lfsr_file_t file; lfsr_file_open(&lfs, &file, "beaver", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { lfsr_mkdir(&lfs, "beaver") => 0; } // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "beaver", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "beaver", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "beaver", 'c', c, strlen(c)) => 0; // move file lfsr_rename(&lfs, "beaver", path) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, path, 'a') => strlen(a); lfsr_sizeattr(&lfs, path, 'b') => strlen(b); lfsr_sizeattr(&lfs, path, 'c') => strlen(c); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } lfsr_unmount(&lfs) => 0; ''' # fuzz attrs mixed with file moves and removes [cases.test_attrs_mvrm_fuzz_fuzz] # type of file to attach attrs to # FILETYPE=0 => regular file # FILETYPE=1 => directory defines.FILETYPE = [0, 1] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create N files for (lfs_size_t i = 0; i < N; i++) { char path[256]; // create a file? if (FILETYPE == 0) { sprintf(path, "cat%03x", i); lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { sprintf(path, "armadillo%03x", i); lfsr_mkdir(&lfs, path) => 0; } } // set up a simulation to compare against uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t)); memset(sim_prngs, 0, N*M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 4; // create an attr? if (op == 0) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // create the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } lfsr_setattr(&lfs, path, a, wbuf, SIZE) => 0; // update our sim sim_prngs[x*M+a] = wprng; // remove an attr? } else if (op == 1) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr if (sim_prngs[x*M+a]) { lfsr_removeattr(&lfs, path, a) => 0; } else { lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR; } // update our sim sim_prngs[x*M+a] = 0; // remove a file? } else if (op == 2) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // remove the file lfsr_remove(&lfs, path) => 0; // but recreate the file so we always have something to // attach attrs to // create a file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { sprintf(path, "armadillo%03x", x); lfsr_mkdir(&lfs, path) => 0; } // update our sim memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t)); // rename a file? } else if (op == 3) { // choose two files lfs_size_t x = TEST_PRNG(&prng) % N; lfs_size_t y = TEST_PRNG(&prng) % N; char path[256]; char path_[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); sprintf(path_, "cat%03x", y); // dir? } else { sprintf(path, "armadillo%03x", x); sprintf(path_, "armadillo%03x", y); } // rename the file lfsr_rename(&lfs, path, path_) => 0; if (x != y) { // but recreate the file so we always have something to // attach attrs to // create a file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); lfsr_file_t file; lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; lfsr_file_write(&lfs, &file, "nyan", strlen("nyan")) => strlen("nyan"); lfsr_file_close(&lfs, &file) => 0; // create a dir? } else { sprintf(path, "armadillo%03x", x); lfsr_mkdir(&lfs, path) => 0; } // update our sim memcpy(&sim_prngs[y*M], &sim_prngs[x*M], M*sizeof(uint32_t)); memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t)); } } } for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } for (lfs_size_t x = 0; x < N; x++) { char path[256]; // file? if (FILETYPE == 0) { sprintf(path, "cat%03x", x); // dir? } else { sprintf(path, "armadillo%03x", x); } // try getting each attr size for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { lfsr_sizeattr(&lfs, path, a) => SIZE; } else { lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR; } } // try reading each attr for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } else { uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } } } } // clean up sim/lfs free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' ## Tests involving file-attached attrs # test that file-attached attrs are read correctly [cases.test_attrs_fattr_get] defines.MODE = ['LFS_A_RDONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with these attrs uint8_t a_buf[256]; lfs_ssize_t a_size = -1; uint8_t b_buf[256]; lfs_ssize_t b_size = -1; uint8_t c_buf[256]; lfs_ssize_t c_size = -1; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = sizeof(a_buf), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = sizeof(b_buf), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = sizeof(c_buf), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size == strlen(a)); } assert(memcmp(a_buf, a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size == strlen(b)); } assert(memcmp(b_buf, b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size == strlen(c)); } assert(memcmp(c_buf, c, strlen(c)) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # test that truncate attrs will work [cases.test_attrs_fattr_trunc] defines.MODE = ['LFS_A_RDONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] defines.BUFSIZE = [1, 4, 7] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with these attrs, marking bufs so we can // detect overflow uint8_t a_buf[256]; a_buf[BUFSIZE] = '!'; lfs_ssize_t a_size = -1; uint8_t b_buf[256]; b_buf[BUFSIZE] = '!'; lfs_ssize_t b_size = -1; uint8_t c_buf[256]; c_buf[BUFSIZE] = '!'; lfs_ssize_t c_size = -1; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = BUFSIZE, .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = BUFSIZE, .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = BUFSIZE, .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; // did we read the attrs correctly? no overflow? if (MUTSIZE) { assert(a_size == BUFSIZE); } assert(memcmp(a_buf, a, BUFSIZE) == 0); assert(a_buf[BUFSIZE] == '!'); if (MUTSIZE) { assert(b_size == BUFSIZE); } assert(memcmp(b_buf, b, BUFSIZE) == 0); assert(a_buf[BUFSIZE] == '!'); if (MUTSIZE) { assert(c_size == BUFSIZE); } assert(memcmp(c_buf, c, BUFSIZE) == 0); assert(a_buf[BUFSIZE] == '!'); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # test that missing attrs are read correctly [cases.test_attrs_fattr_noattr] defines.MODE = ['LFS_A_RDONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // try opening a file with missing attrs uint8_t a_buf[256]; lfs_ssize_t a_size = -1; uint8_t b_buf[256]; lfs_ssize_t b_size = -1; uint8_t c_buf[256]; lfs_ssize_t c_size = -1; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = sizeof(a_buf), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = sizeof(b_buf), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = sizeof(c_buf), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(b_size == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(c_size == LFS_ERR_NOATTR); } lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # catch LFS_O_TRUNC mistakes, this has created issues before [cases.test_attrs_fattr_otrunc] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with these attrs uint8_t a_buf[256]; lfs_ssize_t a_size = -1; uint8_t b_buf[256]; lfs_ssize_t b_size = -1; uint8_t c_buf[256]; lfs_ssize_t c_size = -1; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = sizeof(a_buf), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = sizeof(b_buf), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = sizeof(c_buf), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE | LFS_O_TRUNC, &filecfg) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size == strlen(a)); } assert(memcmp(a_buf, a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size == strlen(b)); } assert(memcmp(b_buf, b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size == strlen(c)); } assert(memcmp(c_buf, c, strlen(c)) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # uncreat files should zero attributes [cases.test_attrs_fattr_uncreat] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // _don't_ create a file // try opening a file with these attrs uint8_t a_buf[256]; lfs_ssize_t a_size = -1; uint8_t b_buf[256]; lfs_ssize_t b_size = -1; uint8_t c_buf[256]; lfs_ssize_t c_size = -1; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = sizeof(a_buf), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = sizeof(b_buf), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = sizeof(c_buf), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_t file; lfsr_file_opencfg(&lfs, &file, "cat", MODE | LFS_O_CREAT, &filecfg) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(b_size == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(c_size == LFS_ERR_NOATTR); } lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # test that file-attached attrs are written correctly [cases.test_attrs_fattr_set] defines.MODE = ['LFS_A_WRONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; const char *b = "One can prepared coconut pecan frosting."; const char *c = "Three slash four cup vegetable oil."; // try opening a file with attrs uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; // set the attrs memcpy(a_buf, a, strlen(a)); memcpy(b_buf, b, strlen(b)); memcpy(c_buf, c, strlen(c)); a_size = strlen(a); b_size = strlen(b); c_size = strlen(c); // write and close our file to write the attrs out to disk lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that we can update existing attrs [cases.test_attrs_fattr_update] defines.MODE = ['LFS_A_WRONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; if (MODE == LFS_A_RDWR && MUTSIZE) { // did we read the attrs correctly? assert(a_size == strlen(a)); assert(memcmp(a_buf, a, strlen(a)) == 0); assert(b_size == strlen(b)); assert(memcmp(b_buf, b, strlen(b)) == 0); assert(c_size == strlen(c)); assert(memcmp(c_buf, c, strlen(c)) == 0); } // update the attrs with new values memcpy(a_buf, a_, strlen(a_)); memcpy(b_buf, b_, strlen(b_)); memcpy(c_buf, c_, strlen(c_)); a_size = strlen(a_); b_size = strlen(b_); c_size = strlen(c_); // write and close our file to write the attrs out to disk lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a_); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b_); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c_); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_); assert(memcmp(rbuf, a_, strlen(a_)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_); assert(memcmp(rbuf, b_, strlen(b_)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_); assert(memcmp(rbuf, c_, strlen(c_)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that we can remove attrs [cases.test_attrs_fattr_remove] defines.MODE = ['LFS_A_WRONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with attrs uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? (lfs_ssize_t)sizeof(a_buf) : LFS_ERR_NOATTR, .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? (lfs_ssize_t)sizeof(b_buf) : LFS_ERR_NOATTR, .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? (lfs_ssize_t)sizeof(c_buf) : LFS_ERR_NOATTR, .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; if (MODE == LFS_A_RDWR && MUTSIZE) { // did we read the attrs correctly? assert(a_size == strlen(a)); assert(memcmp(a_buf, a, strlen(a)) == 0); assert(b_size == strlen(b)); assert(memcmp(b_buf, b, strlen(b)) == 0); assert(c_size == strlen(c)); assert(memcmp(c_buf, c, strlen(c)) == 0); } // mark the attrs as removed a_size = LFS_ERR_NOATTR; b_size = LFS_ERR_NOATTR; c_size = LFS_ERR_NOATTR; // write and close our file to write the attrs out to disk lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, "cat", 'b') => LFS_ERR_NOATTR; lfsr_sizeattr(&lfs, "cat", 'c') => LFS_ERR_NOATTR; // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR; } lfsr_unmount(&lfs) => 0; ''' # test that wronly attrs are not read from disk [cases.test_attrs_fattr_wronly] defines.MODE = ['LFS_A_WRONLY'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with new wronly attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; uint8_t a_buf[256]; memcpy(a_buf, a_, strlen(a_)); lfs_ssize_t a_size = strlen(a_); uint8_t b_buf[256]; memcpy(b_buf, b_, strlen(b_)); lfs_ssize_t b_size = strlen(b_); uint8_t c_buf[256]; memcpy(c_buf, c_, strlen(c_)); lfs_ssize_t c_size = strlen(c_); struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", LFS_O_RDWR, &filecfg) => 0; // open should have had no effect on our wronly attrs assert(a_size == strlen(a_)); assert(memcmp(a_buf, a_, strlen(a_)) == 0); assert(b_size == strlen(b_)); assert(memcmp(b_buf, b_, strlen(b_)) == 0); assert(c_size == strlen(c_)); assert(memcmp(c_buf, c_, strlen(c_)) == 0); // write and close our file to write the attrs out to disk lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a_); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b_); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c_); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_); assert(memcmp(rbuf, a_, strlen(a_)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_); assert(memcmp(rbuf, b_, strlen(b_)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_); assert(memcmp(rbuf, c_, strlen(c_)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that rdonly attrs have no effect on disk [cases.test_attrs_fattr_rdonly] defines.MODE = ['LFS_A_RDONLY'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with rdonly attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", LFS_O_RDWR, &filecfg) => 0; if (MUTSIZE) { // did we read the attrs correctly? assert(a_size == strlen(a)); assert(memcmp(a_buf, a, strlen(a)) == 0); assert(b_size == strlen(b)); assert(memcmp(b_buf, b, strlen(b)) == 0); assert(c_size == strlen(c)); assert(memcmp(c_buf, c, strlen(c)) == 0); } // update the attrs with new values memcpy(a_buf, a_, strlen(a_)); memcpy(b_buf, b_, strlen(b_)); memcpy(c_buf, c_, strlen(c_)); a_size = strlen(a_); b_size = strlen(b_); c_size = strlen(c_); // write and close our file, this should have no effect on attrs lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that attrs are written correctly even if there are no file changes [cases.test_attrs_fattr_noop] defines.MODE = ['LFS_A_WRONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; if (MODE == LFS_A_RDWR && MUTSIZE) { // did we read the attrs correctly? assert(a_size == strlen(a)); assert(memcmp(a_buf, a, strlen(a)) == 0); assert(b_size == strlen(b)); assert(memcmp(b_buf, b, strlen(b)) == 0); assert(c_size == strlen(c)); assert(memcmp(c_buf, c, strlen(c)) == 0); } // update the attrs with new values memcpy(a_buf, a_, strlen(a_)); memcpy(b_buf, b_, strlen(b_)); memcpy(c_buf, c_, strlen(c_)); a_size = strlen(a_); b_size = strlen(b_); c_size = strlen(c_); // close our file without any data changes, this should still update // our attrs! lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a_); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b_); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c_); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_); assert(memcmp(rbuf, a_, strlen(a_)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_); assert(memcmp(rbuf, b_, strlen(b_)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_); assert(memcmp(rbuf, c_, strlen(c_)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that lazy attrs aren't written _until_ there are file changes [cases.test_attrs_fattr_lazy] defines.MODE = ['LFS_A_WRONLY', 'LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // try opening a file with attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; uint8_t a_buf[256]; lfs_ssize_t a_size; uint8_t b_buf[256]; lfs_ssize_t b_size; uint8_t c_buf[256]; lfs_ssize_t c_size; struct lfs_attr attrs[] = { { .type = 'a', .flags = MODE | LFS_A_LAZY, .buffer = a_buf, .buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_), .size = (MUTSIZE) ? &a_size : NULL, }, { .type = 'b', .flags = MODE | LFS_A_LAZY, .buffer = b_buf, .buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_), .size = (MUTSIZE) ? &b_size : NULL, }, { .type = 'c', .flags = MODE | LFS_A_LAZY, .buffer = c_buf, .buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_), .size = (MUTSIZE) ? &c_size : NULL, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file, "cat", MODE, &filecfg) => 0; if (MODE == LFS_A_RDWR && MUTSIZE) { // did we read the attrs correctly? assert(a_size == strlen(a)); assert(memcmp(a_buf, a, strlen(a)) == 0); assert(b_size == strlen(b)); assert(memcmp(b_buf, b, strlen(b)) == 0); assert(c_size == strlen(c)); assert(memcmp(c_buf, c, strlen(c)) == 0); } // update the attrs with new values memcpy(a_buf, a_, strlen(a_)); memcpy(b_buf, b_, strlen(b_)); memcpy(c_buf, c_, strlen(c_)); a_size = strlen(a_); b_size = strlen(b_); c_size = strlen(c_); // sync our file without any data changes, this should have no // effect because our attrs are lazy lfsr_file_sync(&lfs, &file) => 0; // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a); assert(memcmp(rbuf, a, strlen(a)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b); assert(memcmp(rbuf, b, strlen(b)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c); assert(memcmp(rbuf, c, strlen(c)) == 0); // now write some data and close our file, this should update // the attrs now lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // try getting the attr sizes lfsr_sizeattr(&lfs, "cat", 'a') => strlen(a_); lfsr_sizeattr(&lfs, "cat", 'b') => strlen(b_); lfsr_sizeattr(&lfs, "cat", 'c') => strlen(c_); // try reading the attrs uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_); assert(memcmp(rbuf, a_, strlen(a_)) == 0); lfsr_getattr(&lfs, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_); assert(memcmp(rbuf, b_, strlen(b_)) == 0); lfsr_getattr(&lfs, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_); assert(memcmp(rbuf, c_, strlen(c_)) == 0); } lfsr_unmount(&lfs) => 0; ''' # test that attr updates are broadcast to other file handles [cases.test_attrs_fattr_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // update _one_ file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[1], a_, strlen(a_)); memcpy(b_buf[1], b_, strlen(b_)); memcpy(c_buf[1], c_, strlen(c_)); a_size[1] = strlen(a_); b_size[1] = strlen(b_); c_size[1] = strlen(c_); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[1], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[1]) => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that attr removes are broadcast to other file handles [cases.test_attrs_fattr_remove_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // remove _one_ file's attrs a_size[1] = LFS_ERR_NOATTR; b_size[1] = LFS_ERR_NOATTR; c_size[1] = LFS_ERR_NOATTR; // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[1], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[1]) => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(b_size[i] == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(c_size[i] == LFS_ERR_NOATTR); } } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that setattr can also broadcast [cases.test_attrs_fattr_setattr_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // update attrs with setattr const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; lfsr_setattr(&lfs, "cat", 'a', a_, strlen(a_)) => 0; const char *b_ = "Three slash four cup butter or margarine."; lfsr_setattr(&lfs, "cat", 'b', b_, strlen(b_)) => 0; const char *c_ = "One and two third cups granulated sugar."; lfsr_setattr(&lfs, "cat", 'c', c_, strlen(c_)) => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that attr removes are broadcast to other file handles [cases.test_attrs_fattr_removeattr_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // remove attrs with lfsr_removeattr lfsr_removeattr(&lfs, "cat", 'a') => 0; lfsr_removeattr(&lfs, "cat", 'b') => 0; lfsr_removeattr(&lfs, "cat", 'c') => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(b_size[i] == LFS_ERR_NOATTR); } if (MUTSIZE) { assert(c_size[i] == LFS_ERR_NOATTR); } } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that attr broadcasts do _not_ update wronly attrs [cases.test_attrs_fattr_wronly_no_receive] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // make one attr set wronly attrs[0][0].flags = LFS_A_WRONLY; attrs[0][1].flags = LFS_A_WRONLY; attrs[0][2].flags = LFS_A_WRONLY; // update another file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[2], a_, strlen(a_)); memcpy(b_buf[2], b_, strlen(b_)); memcpy(c_buf[2], c_, strlen(c_)); a_size[2] = strlen(a_); b_size[2] = strlen(b_); c_size[2] = strlen(c_); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[2], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[2]) => 0; // were attrs broadcasted to the other files? but not our wronly attrs? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } } // update attrs with setattr const char *a__ = "Two cups all-purpose flower."; lfsr_setattr(&lfs, "cat", 'a', a__, strlen(a__)) => 0; const char *b__ = "Dont forget garnishes such as:"; lfsr_setattr(&lfs, "cat", 'b', b__, strlen(b__)) => 0; const char *c__ = "Fish-shaped crackers."; lfsr_setattr(&lfs, "cat", 'c', c__, strlen(c__)) => 0; // were attrs broadcasted to the other files? but not our wronly attrs? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a__)); } assert(memcmp(a_buf[i], a__, strlen(a__)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b__)); } assert(memcmp(b_buf[i], b__, strlen(b__)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c__)); } assert(memcmp(c_buf[i], c__, strlen(c__)) == 0); } } // if we sync our wronly file we should get the original attrs back lfsr_file_sync(&lfs, &file[0]) => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that attr broadcasts do _not_ broadcast rdonly attrs [cases.test_attrs_fattr_rdonly_no_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // make one attr set rdonly attrs[0][0].flags = LFS_A_RDONLY; attrs[0][1].flags = LFS_A_RDONLY; attrs[0][2].flags = LFS_A_RDONLY; // change the rdonly file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[0], a_, strlen(a_)); memcpy(b_buf[0], b_, strlen(b_)); memcpy(c_buf[0], c_, strlen(c_)); a_size[0] = strlen(a_); b_size[0] = strlen(b_); c_size[0] = strlen(c_); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[0], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[0]) => 0; // rdonly attrs should not have been broadcasted for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } } // sync any file to update our rdonly attrs lfsr_file_sync(&lfs, &file[2]) => 0; // reset attrs? for (lfs_size_t i = 0; i < 3; i++) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that desync files do _not_ receive attr broadcasts [cases.test_attrs_fattr_desync_no_receive] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // make one file desync lfsr_file_desync(&lfs, &file[0]) => 0; // update another file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[2], a_, strlen(a_)); memcpy(b_buf[2], b_, strlen(b_)); memcpy(c_buf[2], c_, strlen(c_)); a_size[2] = strlen(a_); b_size[2] = strlen(b_); c_size[2] = strlen(c_); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[2], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[2]) => 0; // were attrs broadcasted to the other files? but not our desync file? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } } // update attrs with setattr const char *a__ = "Two cups all-purpose flower."; lfsr_setattr(&lfs, "cat", 'a', a__, strlen(a__)) => 0; const char *b__ = "Dont forget garnishes such as:"; lfsr_setattr(&lfs, "cat", 'b', b__, strlen(b__)) => 0; const char *c__ = "Fish-shaped crackers."; lfsr_setattr(&lfs, "cat", 'c', c__, strlen(c__)) => 0; // were attrs broadcasted to the other files? but not our desync file? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a__)); } assert(memcmp(a_buf[i], a__, strlen(a__)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b__)); } assert(memcmp(b_buf[i], b__, strlen(b__)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c__)); } assert(memcmp(c_buf[i], c__, strlen(c__)) == 0); } } // syncing the desync file should broadcast the original attrs lfsr_file_sync(&lfs, &file[0]) => 0; // were attrs broadcasted to the other files? for (lfs_size_t i = 0; i < 3; i++) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that resync files will reread attrs [cases.test_attrs_fattr_resync_receive] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // make one file desync lfsr_file_desync(&lfs, &file[0]) => 0; // update another file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[2], a_, strlen(a_)); memcpy(b_buf[2], b_, strlen(b_)); memcpy(c_buf[2], c_, strlen(c_)); a_size[2] = strlen(a_); b_size[2] = strlen(b_); c_size[2] = strlen(c_); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[2], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[2]) => 0; // were attrs broadcasted to the other files? but not our desync file? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } } // resync the desync file lfsr_file_resync(&lfs, &file[0]) => 0; // reread attrs? for (lfs_size_t i = 0; i < 3; i++) { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that zombie files do _not_ broadcast attrs [cases.test_attrs_fattr_zombie_no_broadcast] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // remove the file lfsr_remove(&lfs, "cat") => 0; // update one file's attrs const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; const char *b_ = "Three slash four cup butter or margarine."; const char *c_ = "One and two third cups granulated sugar."; memcpy(a_buf[1], a_, strlen(a_)); memcpy(b_buf[1], b_, strlen(b_)); memcpy(c_buf[1], c_, strlen(c_)); a_size[1] = strlen(a_); b_size[1] = strlen(b_); c_size[1] = strlen(c_); // attempting to sync should error lfsr_file_sync(&lfs, &file[1]) => LFS_ERR_NOENT; // attempting to resync should error lfsr_file_resync(&lfs, &file[1]) => LFS_ERR_NOENT; // other file unaffected? for (lfs_size_t i = 0; i < 3; i++) { if (i == 1) { if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test that zombie files do _not_ receive attr broadcasts [cases.test_attrs_fattr_zombie_no_receive] defines.MODE = ['LFS_A_RDWR'] defines.MUTSIZE = [false, true] code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file_; lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file_) => 0; // create some attrs const char *a = "One 18.25 ounce package chocolate cake mix."; lfsr_setattr(&lfs, "cat", 'a', a, strlen(a)) => 0; const char *b = "One can prepared coconut pecan frosting."; lfsr_setattr(&lfs, "cat", 'b', b, strlen(b)) => 0; const char *c = "Three slash four cup vegetable oil."; lfsr_setattr(&lfs, "cat", 'c', c, strlen(c)) => 0; // open a couple files with these attrs uint8_t a_buf[3][256]; lfs_ssize_t a_size[3]; uint8_t b_buf[3][256]; lfs_ssize_t b_size[3]; uint8_t c_buf[3][256]; lfs_ssize_t c_size[3]; struct lfs_attr attrs[3][3]; struct lfs_file_config filecfg[3]; lfsr_file_t file[3]; for (lfs_size_t i = 0; i < 3; i++) { attrs[i][0] = (struct lfs_attr){ .type = 'a', .flags = MODE, .buffer = a_buf[i], .buffer_size = sizeof(a_buf[i]), .size = (MUTSIZE) ? &a_size[i] : NULL, }; attrs[i][1] = (struct lfs_attr){ .type = 'b', .flags = MODE, .buffer = b_buf[i], .buffer_size = sizeof(b_buf[i]), .size = (MUTSIZE) ? &b_size[i] : NULL, }; attrs[i][2] = (struct lfs_attr){ .type = 'c', .flags = MODE, .buffer = c_buf[i], .buffer_size = sizeof(c_buf[i]), .size = (MUTSIZE) ? &c_size[i] : NULL, }; filecfg[i] = (struct lfs_file_config){ .attrs = attrs[i], .attr_count = 3, }; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // remove the file lfsr_remove(&lfs, "cat") => 0; // recreate the file lfsr_file_open(&lfs, &file_, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file_, "miao", strlen("miao")) => strlen("miao"); lfsr_file_close(&lfs, &file_) => 0; const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips."; lfsr_setattr(&lfs, "cat", 'a', a_, strlen(a_)) => 0; const char *b_ = "Three slash four cup butter or margarine."; lfsr_setattr(&lfs, "cat", 'b', b_, strlen(b_)) => 0; const char *c_ = "One and two third cups granulated sugar."; lfsr_setattr(&lfs, "cat", 'c', c_, strlen(c_)) => 0; // make sure no attrs were broadcasted to our zombies for (lfs_size_t i = 0; i < 3; i++) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } // reopen a couple files with these attrs for (lfs_size_t i = 0; i < 3; i++) { // leave one file a zombie if (i == 0) { continue; } lfsr_file_close(&lfs, &file[i]) => 0; lfsr_file_opencfg(&lfs, &file[i], "cat", MODE, &filecfg[i]) => 0; // did we read the attrs correctly? if (MUTSIZE) { assert(a_size[i] == strlen(a_)); } assert(memcmp(a_buf[i], a_, strlen(a_)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b_)); } assert(memcmp(b_buf[i], b_, strlen(b_)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c_)); } assert(memcmp(c_buf[i], c_, strlen(c_)) == 0); } // update another file's attrs const char *a__ = "Two cups all-purpose flower."; const char *b__ = "Dont forget garnishes such as:"; const char *c__ = "Fish-shaped crackers."; memcpy(a_buf[2], a__, strlen(a__)); memcpy(b_buf[2], b__, strlen(b__)); memcpy(c_buf[2], c__, strlen(c__)); a_size[2] = strlen(a__); b_size[2] = strlen(b__); c_size[2] = strlen(c__); // write and sync our file to write the attrs out to disk lfsr_file_write(&lfs, &file[2], "nyan", strlen("nyan")) => strlen("nyan"); lfsr_file_sync(&lfs, &file[2]) => 0; // were attrs broadcasted to the other files? but not our zombie file? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a__)); } assert(memcmp(a_buf[i], a__, strlen(a__)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b__)); } assert(memcmp(b_buf[i], b__, strlen(b__)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c__)); } assert(memcmp(c_buf[i], c__, strlen(c__)) == 0); } } // update attrs with setattr const char *a___ = "Fish-shaped candies."; lfsr_setattr(&lfs, "cat", 'a', a___, strlen(a___)) => 0; const char *b___ = "Fish-shaped solid waste."; lfsr_setattr(&lfs, "cat", 'b', b___, strlen(b___)) => 0; const char *c___ = "Fish-shaped dirt."; lfsr_setattr(&lfs, "cat", 'c', c___, strlen(c___)) => 0; // were attrs broadcasted to the other files? but not our zombie file? for (lfs_size_t i = 0; i < 3; i++) { if (i == 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a)); } assert(memcmp(a_buf[i], a, strlen(a)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b)); } assert(memcmp(b_buf[i], b, strlen(b)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c)); } assert(memcmp(c_buf[i], c, strlen(c)) == 0); } else { if (MUTSIZE) { assert(a_size[i] == strlen(a___)); } assert(memcmp(a_buf[i], a___, strlen(a___)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b___)); } assert(memcmp(b_buf[i], b___, strlen(b___)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c___)); } assert(memcmp(c_buf[i], c___, strlen(c___)) == 0); } } // attempting to sync the zombie file should error lfsr_file_sync(&lfs, &file[0]) => LFS_ERR_NOENT; // attempting to resync the zombie file should error lfsr_file_resync(&lfs, &file[0]) => LFS_ERR_NOENT; // other file unaffected? for (lfs_size_t i = 0; i < 3; i++) { if (i != 0) { if (MUTSIZE) { assert(a_size[i] == strlen(a___)); } assert(memcmp(a_buf[i], a___, strlen(a___)) == 0); if (MUTSIZE) { assert(b_size[i] == strlen(b___)); } assert(memcmp(b_buf[i], b___, strlen(b___)) == 0); if (MUTSIZE) { assert(c_size[i] == strlen(c___)); } assert(memcmp(c_buf[i], c___, strlen(c___)) == 0); } } for (lfs_size_t i = 0; i < 3; i++) { lfsr_file_close(&lfs, &file[i]) => 0; } lfsr_unmount(&lfs) => 0; ''' # test the full range of attrs [cases.test_attrs_fattr_all] defines.SIZE = 4 code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create a file lfsr_file_t file; lfsr_file_open(&lfs, &file, "cat", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; // setup our attr uint8_t a_buf[SIZE]; lfs_ssize_t a_size = -1; struct lfs_attr attrs[] = { { .flags = LFS_A_RDWR, .buffer = a_buf, .buffer_size = sizeof(a_buf), .size = &a_size, } }; struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = 1, }; // try creating every attr, this tests any encoding quirks uint32_t prng = 42; for (uint16_t a = 0; a < 0x100; a++) { attrs[0].type = a; // create the attr via open uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_opencfg(&lfs, &file, "cat", LFS_A_RDWR, &filecfg) => 0; memcpy(a_buf, wbuf, SIZE); a_size = SIZE; lfsr_file_close(&lfs, &file) => 0; // try getting the attr size lfsr_sizeattr(&lfs, "cat", a) => SIZE; // try reading the attr uint8_t rbuf[256]; lfsr_getattr(&lfs, "cat", a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); // clobber our in-RAM attr and try reading via open memset(a_buf, 0xcc, sizeof(a_buf)); lfsr_file_opencfg(&lfs, &file, "cat", LFS_A_RDWR, &filecfg) => 0; assert(a_size == SIZE); assert(memcmp(a_buf, wbuf, SIZE) == 0); // remove the attr a_size = LFS_ERR_NOATTR; lfsr_file_close(&lfs, &file) => 0; // make sure attr is removed lfsr_sizeattr(&lfs, "cat", a) => LFS_ERR_NOATTR; } lfsr_unmount(&lfs) => 0; ''' # test creating a bunch of attrs on a bunch of files [cases.test_attrs_fattr_many_many] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.COMPACT = [false, true] defines.GC_FLAGS = 'LFS_GC_COMPACT' defines.GC_STEPS = -1 defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2' if = 'LFS_IFDEF_GC(true, !COMPACT)' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; // create N files with M attrs uint32_t prng = 42; for (lfs_size_t i = 0; i < N; i++) { char path[256]; sprintf(path, "cat%03x", i); uint8_t a_buf[M][SIZE]; struct lfs_attr attrs[M]; for (lfs_size_t j = 0; j < M; j++) { for (lfs_size_t k = 0; k < SIZE; k++) { a_buf[j][k] = 'a' + (TEST_PRNG(&prng) % 26); } attrs[j] = (struct lfs_attr){ .type = j, .flags = LFS_A_WRONLY, .buffer = a_buf[j], .buffer_size = SIZE, }; } struct lfs_file_config filecfg = { .attrs = attrs, .attr_count = M, }; lfsr_file_t file; lfsr_file_opencfg(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT, &filecfg) => 0; lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow"); lfsr_file_close(&lfs, &file) => 0; } // try compacting? #ifdef LFS_GC if (COMPACT) { lfsr_fs_gc(&lfs) => 0; } #endif for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } prng = 42; for (lfs_size_t x = 0; x < N; x++) { char path[256]; sprintf(path, "cat%03x", x); // try getting the attr sizes for (uint16_t a = 0; a < M; a++) { lfsr_sizeattr(&lfs, path, a) => SIZE; } // try reading the attrs for (uint16_t a = 0; a < M; a++) { uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } uint8_t rbuf[256]; lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE; assert(memcmp(rbuf, wbuf, SIZE) == 0); } } } lfsr_unmount(&lfs) => 0; ''' # fuzz attrs on multiple files [cases.test_attrs_fattr_fuzz_fuzz] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; uint8_t a_buf[N][M][SIZE]; lfs_ssize_t a_size[N][M]; struct lfs_attr attrs[N][M]; struct lfs_file_config filecfg[N]; lfsr_file_t file[N]; // create N files for (lfs_size_t x = 0; x < N; x++) { char path[256]; sprintf(path, "cat%03x", x); for (lfs_size_t a = 0; a < M; a++) { attrs[x][a] = (struct lfs_attr){ .type = a, .flags = LFS_A_RDWR, .buffer = a_buf[x][a], .buffer_size = SIZE, .size = &a_size[x][a], }; } filecfg[x] = (struct lfs_file_config){ .attrs = attrs[x], .attr_count = M, }; lfsr_file_opencfg(&lfs, &file[x], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL, &filecfg[x]) => 0; lfsr_file_write(&lfs, &file[x], "meow", strlen("meow")) => strlen("meow"); lfsr_file_sync(&lfs, &file[x]) => 0; } // set up a simulation to compare against uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t)); memset(sim_prngs, 0, N*M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 2; // create an attr? if (op == 0) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // update the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } memcpy(a_buf[x][a], wbuf, SIZE); a_size[x][a] = SIZE; lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim sim_prngs[x*M+a] = wprng; // remove an attr? } else if (op == 1) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr a_size[x][a] = LFS_ERR_NOATTR; lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim sim_prngs[x*M+a] = 0; } } // check attrs on all file handles for (lfs_size_t x = 0; x < N; x++) { for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { assert(a_size[x][a] == SIZE); uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[x][a], wbuf, SIZE) == 0); } else { assert(a_size[x][a] == LFS_ERR_NOATTR); } } } // clean up sim/lfs for (lfs_size_t x = 0; x < N; x++) { lfsr_file_close(&lfs, &file[x]) => 0; } free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # fuzz attrs on multiple overlapping files [cases.test_attrs_fattr_fuzz_fuzz_fuzz] defines.N = 64 defines.H = 4 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; uint8_t a_buf[N][H][M][SIZE]; lfs_ssize_t a_size[N][H][M]; struct lfs_attr attrs[N][H][M]; struct lfs_file_config filecfg[N][H]; lfsr_file_t file[N][H]; // create N files for (lfs_size_t x = 0; x < N; x++) { char path[256]; sprintf(path, "cat%03x", x); for (lfs_size_t y = 0; y < H; y++) { for (lfs_size_t a = 0; a < M; a++) { attrs[x][y][a] = (struct lfs_attr){ .type = a, .flags = LFS_A_RDWR, .buffer = a_buf[x][y][a], .buffer_size = SIZE, .size = &a_size[x][y][a], }; } filecfg[x][y] = (struct lfs_file_config){ .attrs = attrs[x][y], .attr_count = M, }; lfsr_file_opencfg(&lfs, &file[x][y], path, LFS_O_WRONLY | LFS_O_CREAT, &filecfg[x][y]) => 0; lfsr_file_write(&lfs, &file[x][y], "meow", strlen("meow")) => strlen("meow"); lfsr_file_sync(&lfs, &file[x][y]) => 0; } } // set up a simulation to compare against uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t)); memset(sim_prngs, 0, N*M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 2; // create an attr? if (op == 0) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose a file handle lfs_size_t y = TEST_PRNG(&prng) % H; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // update the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } memcpy(a_buf[x][y][a], wbuf, SIZE); a_size[x][y][a] = SIZE; lfsr_file_sync(&lfs, &file[x][y]) => 0; // update our sim sim_prngs[x*M+a] = wprng; // remove an attr? } else if (op == 1) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose a file handle lfs_size_t y = TEST_PRNG(&prng) % H; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr a_size[x][y][a] = LFS_ERR_NOATTR; lfsr_file_sync(&lfs, &file[x][y]) => 0; // update our sim sim_prngs[x*M+a] = 0; } } // check attrs on all file handles for (lfs_size_t x = 0; x < N; x++) { for (lfs_size_t y = 0; y < H; y++) { for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { assert(a_size[x][y][a] == SIZE); uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[x][y][a], wbuf, SIZE) == 0); } else { assert(a_size[x][y][a] == LFS_ERR_NOATTR); } } } } // clean up sim/lfs for (lfs_size_t x = 0; x < N; x++) { for (lfs_size_t y = 0; y < M; y++) { lfsr_file_close(&lfs, &file[x][y]) => 0; } } free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # fuzz file-attached attrs mixed with file moves and removes [cases.test_attrs_fattr_mvrm_fuzz_fuzz] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' code = ''' lfs_t lfs; lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; uint8_t a_buf[N][M][SIZE]; lfs_ssize_t a_size[N][M]; struct lfs_attr attrs[N][M]; struct lfs_file_config filecfg[N]; lfsr_file_t file[N]; // create N files for (lfs_size_t x = 0; x < N; x++) { char path[256]; sprintf(path, "cat%03x", x); for (lfs_size_t a = 0; a < M; a++) { attrs[x][a] = (struct lfs_attr){ .type = a, .flags = LFS_A_RDWR, .buffer = a_buf[x][a], .buffer_size = SIZE, .size = &a_size[x][a], }; } filecfg[x] = (struct lfs_file_config){ .attrs = attrs[x], .attr_count = M, }; lfsr_file_opencfg(&lfs, &file[x], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL, &filecfg[x]) => 0; lfsr_file_write(&lfs, &file[x], "meow", strlen("meow")) => strlen("meow"); lfsr_file_sync(&lfs, &file[x]) => 0; } // set up a simulation to compare against uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t)); memset(sim_prngs, 0, N*M*sizeof(uint32_t)); uint32_t prng = SEED; for (lfs_size_t i = 0; i < OPS; i++) { // choose which operation to do uint8_t op = TEST_PRNG(&prng) % 4; // create an attr? if (op == 0) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // choose a prng uint32_t wprng = TEST_PRNG(&prng); // update the attr uint32_t wprng_ = wprng; uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26); } memcpy(a_buf[x][a], wbuf, SIZE); a_size[x][a] = SIZE; lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim sim_prngs[x*M+a] = wprng; // remove an attr? } else if (op == 1) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; // choose an attr uint8_t a = TEST_PRNG(&prng) % M; // remove the attr a_size[x][a] = LFS_ERR_NOATTR; lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim sim_prngs[x*M+a] = 0; // remove a file? } else if (op == 2) { // choose a file lfs_size_t x = TEST_PRNG(&prng) % N; char path[256]; sprintf(path, "cat%03x", x); // remove the file lfsr_remove(&lfs, path) => 0; // check that remove had no affect on open attrs for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { assert(a_size[x][a] == SIZE); uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[x][a], wbuf, SIZE) == 0); } else { assert(a_size[x][a] == LFS_ERR_NOATTR); } } // but recreate the file so we always have something to // attach attrs to lfsr_file_close(&lfs, &file[x]) => 0; sprintf(path, "cat%03x", x); lfsr_file_opencfg(&lfs, &file[x], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL, &filecfg[x]) => 0; lfsr_file_write(&lfs, &file[x], "miao", strlen("miao")) => strlen("miao"); lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t)); // rename a file? } else if (op == 3) { // choose two files lfs_size_t x = TEST_PRNG(&prng) % N; lfs_size_t y = TEST_PRNG(&prng) % N; char path[256]; char path_[256]; sprintf(path, "cat%03x", x); sprintf(path_, "cat%03x", y); // rename the file lfsr_rename(&lfs, path, path_) => 0; // check that rename had no affect on open attrs for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { assert(a_size[x][a] == SIZE); uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[x][a], wbuf, SIZE) == 0); } else { assert(a_size[x][a] == LFS_ERR_NOATTR); } } for (uint16_t a = 0; a < M; a++) { if (sim_prngs[y*M+a]) { assert(a_size[y][a] == SIZE); uint32_t wprng_ = sim_prngs[y*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[y][a], wbuf, SIZE) == 0); } else { assert(a_size[y][a] == LFS_ERR_NOATTR); } } if (x != y) { // but recreate the file so we always have something to // attach attrs to lfsr_file_close(&lfs, &file[y]) => 0; sprintf(path, "cat%03x", y); lfsr_file_opencfg(&lfs, &file[y], path, LFS_O_WRONLY, &filecfg[y]) => 0; lfsr_file_close(&lfs, &file[x]) => 0; sprintf(path, "cat%03x", x); lfsr_file_opencfg(&lfs, &file[x], path, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL, &filecfg[x]) => 0; lfsr_file_write(&lfs, &file[x], "nyan", strlen("nyan")) => strlen("nyan"); lfsr_file_sync(&lfs, &file[x]) => 0; // update our sim memcpy(&sim_prngs[y*M], &sim_prngs[x*M], M*sizeof(uint32_t)); memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t)); } } } // check attrs on all file handles for (lfs_size_t x = 0; x < N; x++) { for (uint16_t a = 0; a < M; a++) { if (sim_prngs[x*M+a]) { assert(a_size[x][a] == SIZE); uint32_t wprng_ = sim_prngs[x*M+a]; uint8_t wbuf[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26); } assert(memcmp(a_buf[x][a], wbuf, SIZE) == 0); } else { assert(a_size[x][a] == LFS_ERR_NOATTR); } } } // clean up sim/lfs for (lfs_size_t x = 0; x < N; x++) { lfsr_file_close(&lfs, &file[x]) => 0; } free(sim_prngs); lfsr_unmount(&lfs) => 0; ''' # test that file-attached attrs are actually atomic [cases.test_attrs_fattr_pl_fuzz_fuzz] defines.N = 64 defines.M = 4 defines.SIZE = 4 defines.OPS = '4*N*M' defines.SEED = 'range(20)' fuzz = 'SEED' reentrant = true code = ''' // format once per test lfs_t lfs; int err = lfsr_mount(&lfs, LFS_M_RDWR, CFG); if (err) { lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // keep some test state on disk to survive powerloss typedef struct fuzz_state { lfs_size_t i; uint32_t prng; } fuzz_state_t; lfsr_file_t state_file; lfsr_file_open(&lfs, &state_file, "state", LFS_O_RDWR | LFS_O_CREAT) => 0; fuzz_state_t state; if (lfsr_file_size(&lfs, &state_file) == 0) { state.i = 0; state.prng = SEED; } else { lfsr_file_read(&lfs, &state_file, &state, sizeof(state)) => sizeof(state); } for (; state.i < OPS; state.i++) { // choose a random file lfs_size_t x = TEST_PRNG(&state.prng) % N; char path[256]; sprintf(path, "cat%03x", x); // the invariant we hold here is that attrs are some increment // of the file data mod 26, this should catch any issues with // atomically updating attrs attached to files // try to open the file with attrs uint8_t a_buf[M][SIZE]; lfs_ssize_t a_size[M]; struct lfs_attr attrs[M]; struct lfs_file_config filecfg; lfsr_file_t file; for (lfs_size_t a = 0; a < M; a++) { attrs[a] = (struct lfs_attr){ .type = a, .flags = LFS_A_RDWR, .buffer = a_buf[a], .buffer_size = SIZE, .size = &a_size[a], }; } filecfg = (struct lfs_file_config){ .attrs = attrs, .attr_count = M, }; lfsr_file_opencfg(&lfs, &file, path, LFS_O_RDWR | LFS_O_CREAT, &filecfg) => 0; // if the file exists, check our invariant if (lfsr_file_size(&lfs, &file) > 0) { uint8_t wbuf[SIZE]; lfsr_file_read(&lfs, &file, wbuf, SIZE) => SIZE; for (lfs_size_t a = 0; a < M; a++) { assert(a_size[a] == SIZE); for (lfs_size_t j = 0; j < SIZE; j++) { assert(a_buf[a][j] == 'a' + (((wbuf[j]-'a') + a) % 26)); } } } // choose a new seed and rewrite our file uint32_t wprng = TEST_PRNG(&state.prng); uint8_t wbuf[SIZE]; for (lfs_size_t j = 0; j < SIZE; j++) { wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26); } lfsr_file_rewind(&lfs, &file) => 0; lfsr_file_write(&lfs, &file, wbuf, SIZE) => SIZE; // and update attrs for (lfs_size_t a = 0; a < M; a++) { a_size[a] = SIZE; for (lfs_size_t j = 0; j < SIZE; j++) { a_buf[a][j] = 'a' + (((wbuf[j]-'a') + a) % 26); } } // and sync/close lfsr_file_close(&lfs, &file) => 0; // update our state file lfsr_file_rewind(&lfs, &state_file) => 0; lfsr_file_write(&lfs, &state_file, &state, sizeof(state)) => sizeof(state); lfsr_file_sync(&lfs, &state_file) => 0; } // go ahead and close our state file in case we remount lfsr_file_close(&lfs, &state_file) => 0; for (int remount = 0; remount < 2; remount++) { // remount? if (remount) { lfsr_unmount(&lfs) => 0; lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0; } // check that our invariant was held in all files for (lfs_size_t x = 0; x < N; x++) { char path[256]; sprintf(path, "cat%03x", x); uint8_t a_buf[M][SIZE]; lfs_ssize_t a_size[M]; struct lfs_attr attrs[M]; struct lfs_file_config filecfg; lfsr_file_t file; for (lfs_size_t a = 0; a < M; a++) { attrs[a] = (struct lfs_attr){ .type = a, .flags = LFS_A_RDWR, .buffer = a_buf[a], .buffer_size = SIZE, .size = &a_size[a], }; } filecfg = (struct lfs_file_config){ .attrs = attrs, .attr_count = M, }; int err = lfsr_file_opencfg(&lfs, &file, path, LFS_O_RDONLY, &filecfg); assert(!err || err == LFS_ERR_NOENT); if (err == LFS_ERR_NOENT) { continue; } // if the file exists, check our invariant uint8_t wbuf[SIZE]; lfsr_file_read(&lfs, &file, wbuf, SIZE) => SIZE; for (lfs_size_t a = 0; a < M; a++) { assert(a_size[a] == SIZE); for (lfs_size_t j = 0; j < SIZE; j++) { assert(a_buf[a][j] == 'a' + (((wbuf[j]-'a') + a) % 26)); } } lfsr_file_close(&lfs, &file) => 0; } } lfsr_unmount(&lfs) => 0; ''' #[cases.test_attrs_get_set] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "hello") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; # lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # uint8_t buffer[1024]; # memset(buffer, 0, sizeof(buffer)); # lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0; # lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0; # lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "bbbbbb", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "hello", 'B', "", 0) => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_removeattr(&lfs, "hello", 'B') => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => LFS_ERR_NOATTR; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "dddddd", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "eee\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; # lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0; # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9; # lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5; # # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # memset(buffer, 0, sizeof(buffer)); # lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9; # lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "fffffffff", 9) => 0; # memcmp(buffer+13, "ccccc", 5) => 0; # # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); # memcmp(buffer, "hello", strlen("hello")) => 0; # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; #''' # #[cases.test_attrs_get_set_root] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "hello") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; # lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # uint8_t buffer[1024]; # memset(buffer, 0, sizeof(buffer)); # lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0; # lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0; # lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "bbbbbb", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "/", 'B', "", 0) => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_removeattr(&lfs, "/", 'B') => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => LFS_ERR_NOATTR; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "/", 'B', "dddddd", 6) => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "dddddd", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "/", 'B', "eee", 3) => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "eee\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # lfs_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC; # lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0; # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9; # lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5; # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # memset(buffer, 0, sizeof(buffer)); # lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4; # lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9; # lfs_getattr(&lfs, "/", 'C', buffer+13, 5) => 5; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "fffffffff", 9) => 0; # memcmp(buffer+13, "ccccc", 5) => 0; # # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); # memcmp(buffer, "hello", strlen("hello")) => 0; # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; #''' # #[cases.test_attrs_get_set_file] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "hello") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; # lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # uint8_t buffer[1024]; # memset(buffer, 0, sizeof(buffer)); # struct lfs_attr attrs1[] = { # {'A', buffer, 4}, # {'B', buffer+4, 6}, # {'C', buffer+10, 5}, # }; # struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; # # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; # memcpy(buffer, "aaaa", 4); # memcpy(buffer+4, "bbbbbb", 6); # memcpy(buffer+10, "ccccc", 5); # lfs_file_close(&lfs, &file) => 0; # memset(buffer, 0, 15); # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "bbbbbb", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # attrs1[1].size = 0; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # memset(buffer, 0, 15); # attrs1[1].size = 6; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # attrs1[1].size = 6; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; # memcpy(buffer+4, "dddddd", 6); # lfs_file_close(&lfs, &file) => 0; # memset(buffer, 0, 15); # attrs1[1].size = 6; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "dddddd", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # attrs1[1].size = 3; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; # memcpy(buffer+4, "eee", 3); # lfs_file_close(&lfs, &file) => 0; # memset(buffer, 0, 15); # attrs1[1].size = 6; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "eee\0\0\0", 6) => 0; # memcmp(buffer+10, "ccccc", 5) => 0; # # attrs1[0].size = LFS_ATTR_MAX+1; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) # => LFS_ERR_NOSPC; # # struct lfs_attr attrs2[] = { # {'A', buffer, 4}, # {'B', buffer+4, 9}, # {'C', buffer+13, 5}, # }; # struct lfs_file_config cfg2 = {.attrs=attrs2, .attr_count=3}; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDWR, &cfg2) => 0; # memcpy(buffer+4, "fffffffff", 9); # lfs_file_close(&lfs, &file) => 0; # attrs1[0].size = 4; # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg1) => 0; # lfs_file_close(&lfs, &file) => 0; # # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # memset(buffer, 0, sizeof(buffer)); # struct lfs_attr attrs3[] = { # {'A', buffer, 4}, # {'B', buffer+4, 9}, # {'C', buffer+13, 5}, # }; # struct lfs_file_config cfg3 = {.attrs=attrs3, .attr_count=3}; # # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_RDONLY, &cfg3) => 0; # lfs_file_close(&lfs, &file) => 0; # memcmp(buffer, "aaaa", 4) => 0; # memcmp(buffer+4, "fffffffff", 9) => 0; # memcmp(buffer+13, "ccccc", 5) => 0; # # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_RDONLY) => 0; # lfs_file_read(&lfs, &file, buffer, sizeof(buffer)) => strlen("hello"); # memcmp(buffer, "hello", strlen("hello")) => 0; # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; #''' # #[cases.test_attrs_deferred_file] #code = ''' # lfs_t lfs; # lfs_format(&lfs, cfg) => 0; # lfs_mount(&lfs, cfg) => 0; # lfs_mkdir(&lfs, "hello") => 0; # lfs_file_t file; # lfs_file_open(&lfs, &file, "hello/hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; # lfs_file_write(&lfs, &file, "hello", strlen("hello")) => strlen("hello"); # lfs_file_close(&lfs, &file); # lfs_unmount(&lfs) => 0; # # lfs_mount(&lfs, cfg) => 0; # lfs_setattr(&lfs, "hello/hello", 'B', "fffffffff", 9) => 0; # lfs_setattr(&lfs, "hello/hello", 'C', "ccccc", 5) => 0; # # uint8_t buffer[1024]; # memset(buffer, 0, sizeof(buffer)); # struct lfs_attr attrs1[] = { # {'B', "gggg", 4}, # {'C', "", 0}, # {'D', "hhhh", 4}, # }; # struct lfs_file_config cfg1 = {.attrs=attrs1, .attr_count=3}; # # lfs_file_opencfg(&lfs, &file, "hello/hello", LFS_O_WRONLY, &cfg1) => 0; # # lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9; # lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5; # lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => LFS_ERR_NOATTR; # memcmp(buffer, "fffffffff", 9) => 0; # memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0; # memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0; # # lfs_file_sync(&lfs, &file) => 0; # lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4; # lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0; # lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 4; # memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0; # memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0; # memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0; # # lfs_file_close(&lfs, &file) => 0; # lfs_unmount(&lfs) => 0; #'''