Files
littlefs/tests/test_attrs.toml
T
Christopher Haster 5d756fe698 gc: Tweaked lfsr_gc API to be more stateful
Before:

  int lfsr_fs_gc(lfs_t *lfs, lfs_soff_t steps, uint32_t flags);

After:

  int lfsr_gc(lfs_t *lfs);
  int lfsr_gc_setflags(lfs_t *lfs, uint32_t flags);
  int lfsr_gc_setsteps(lfs_t *lfs, lfs_soff_t steps);

---

The interesting thing about the lfsr_gc API is that the caller will
often be very different from whoever configures the system. One example
being an OS calling lfsr_gc in a background loop, while leaving
configuration up to the user.

The idea here, is instead of forcing the OS to come up with its own
stateful system to pass flags to lfsr_gc, we just embed this state in
littlefs directly. The whole point of lfsr_gc is that it's a stateful
system anyways.

Unfortunately this state does require a bit more logic to maintain,
which adds code/ctx cost:

           code          stack          ctx
  before: 37812           2608          752
  after:  37916 (+0.3%)   2608 (+0.0%)  768 (+2.1%)
2025-01-28 14:41:45 -06:00

5105 lines
162 KiB
TOML

# 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'
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?
if (COMPACT) {
lfsr_gc(&lfs) => 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
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'
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?
if (COMPACT) {
lfsr_gc(&lfs) => 0;
}
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'
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?
if (COMPACT) {
lfsr_gc(&lfs) => 0;
}
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;
#'''