Files
littlefs/tests/test_attrs.toml
Christopher Haster 7a57b1e2bd Renamed LFS3_T_COMPACTMETA -> LFS3_T_COMPACT (and gc_compact_thresh)
This effectively reverts 1f824a0:

- LFS3_T_COMPACTMETA -> LFS3_T_COMPACT
- gc_compactmeta_thresh -> gc_compact_thresh

And friends.

After using LFS3_T_COMPACTMETA for a bit, I think it just adds noise
without much value. Especially when next to LFS3_T_LOOKAHEAD,
LFS3_GC_PREERASE, LFS3_M_SYNC, etc.

It's interesting that we already have some very distinct verbs for this
sort of thing based on data type (compact => metadata, garbage-collect
=> disk, compress => data).
2026-01-09 00:01:05 -06:00

5754 lines
182 KiB
TOML

# Custom attribute tests
after = ['test_files', 'test_fsync', 'test_stickynotes']
## 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 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
defines.BUFSIZE = [1, 4, 7]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
// mark rbuf so we can detect overflow
uint8_t rbuf[256];
rbuf[BUFSIZE] = '!';
// try reading truncated attrs
lfs3_getattr(&lfs3, path, 'a', rbuf, BUFSIZE) => BUFSIZE;
assert(memcmp(rbuf, a, BUFSIZE) == 0);
assert(rbuf[BUFSIZE] == '!');
lfs3_getattr(&lfs3, path, 'b', rbuf, BUFSIZE) => BUFSIZE;
assert(memcmp(rbuf, b, BUFSIZE) == 0);
assert(rbuf[BUFSIZE] == '!');
lfs3_getattr(&lfs3, path, 'c', rbuf, BUFSIZE) => BUFSIZE;
assert(memcmp(rbuf, c, BUFSIZE) == 0);
assert(rbuf[BUFSIZE] == '!');
// try reading the full attrs
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test ENOATTR works
[cases.test_attrs_setattr_noattr]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'b') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'c') => LFS3_ERR_NOATTR;
// try reading attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that updating attrs works
[cases.test_attrs_setattr_update]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
# update based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// rewrite some attrs
const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips.";
if (MASK & 0x1) {
lfs3_setattr(&lfs3, path, 'a', a_, strlen(a_)) => 0;
}
const char *b_ = "Three slash four cup butter or margarine.";
if (MASK & 0x2) {
lfs3_setattr(&lfs3, path, 'b', b_, strlen(b_)) => 0;
}
const char *c_ = "One and two third cups granulated sugar.";
if (MASK & 0x4) {
lfs3_setattr(&lfs3, path, 'c', c_, strlen(c_)) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a_);
} else {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b_);
} else {
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c_);
} else {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
}
// try reading attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
} else {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
} else {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
} else {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test removing attrs
[cases.test_attrs_removeattr]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
# remove based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// remove some attrs
if (MASK & 0x1) {
lfs3_removeattr(&lfs3, path, 'a') => 0;
}
if (MASK & 0x2) {
lfs3_removeattr(&lfs3, path, 'b') => 0;
}
if (MASK & 0x4) {
lfs3_removeattr(&lfs3, path, 'c') => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfs3_sizeattr(&lfs3, path, 'a') => LFS3_ERR_NOATTR;
} else {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfs3_sizeattr(&lfs3, path, 'b') => LFS3_ERR_NOATTR;
} else {
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfs3_sizeattr(&lfs3, path, 'c') => LFS3_ERR_NOATTR;
} else {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
}
// try reading the full attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
} else {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
} else {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
} else {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test setting attrs to zero size
#
# this is _not_ the same as removing
#
[cases.test_attrs_setattr_zero]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
# zero based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// remove some attrs
if (MASK & 0x1) {
lfs3_setattr(&lfs3, path, 'a', (const uint8_t*)1, 0) => 0;
}
if (MASK & 0x2) {
lfs3_setattr(&lfs3, path, 'b', (const uint8_t*)1, 0) => 0;
}
if (MASK & 0x4) {
lfs3_setattr(&lfs3, path, 'c', (const uint8_t*)1, 0) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfs3_sizeattr(&lfs3, path, 'a') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfs3_sizeattr(&lfs3, path, 'b') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfs3_sizeattr(&lfs3, path, 'c') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
}
// try reading the full attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test setting attrs to zero size + null
#
# this can trip up naive internal logic
#
[cases.test_attrs_setattr_null]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
# zero based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// remove some attrs
if (MASK & 0x1) {
lfs3_setattr(&lfs3, path, 'a', NULL, 0) => 0;
}
if (MASK & 0x2) {
lfs3_setattr(&lfs3, path, 'b', NULL, 0) => 0;
}
if (MASK & 0x4) {
lfs3_setattr(&lfs3, path, 'c', NULL, 0) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfs3_sizeattr(&lfs3, path, 'a') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfs3_sizeattr(&lfs3, path, 'b') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfs3_sizeattr(&lfs3, path, 'c') => 0;
} else {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
}
// try reading the full attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf))
=> strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf))
=> strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> 0;
} else {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf))
=> strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can set attrs to a size >256
#
# this can trip up internal logic that uses uint8_t sizes
#
[cases.test_attrs_setattr_big]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
# update based on this mask
defines.MASK = 'range(0x8)'
defines.SIZE = [500, 509, 512, 513]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
uint8_t b[1024];
memset(b, 'b', SIZE);
lfs3_setattr(&lfs3, path, 'b', b, SIZE) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// rewrite some attrs
const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips.";
if (MASK & 0x1) {
lfs3_setattr(&lfs3, path, 'a', a_, strlen(a_)) => 0;
}
uint8_t b_[1024];
memset(b_, 'B', SIZE);
if (MASK & 0x2) {
lfs3_setattr(&lfs3, path, 'b', b_, SIZE) => 0;
}
const char *c_ = "One and two third cups granulated sugar.";
if (MASK & 0x4) {
lfs3_setattr(&lfs3, path, 'c', c_, strlen(c_)) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a_);
} else {
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfs3_sizeattr(&lfs3, path, 'b') => SIZE;
} else {
lfs3_sizeattr(&lfs3, path, 'b') => SIZE;
}
if (MASK & 0x4) {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c_);
} else {
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
}
// try reading attrs
uint8_t rbuf[1024];
if (MASK & 0x1) {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
} else {
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, b_, SIZE) == 0);
} else {
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, b, SIZE) == 0);
}
if (MASK & 0x4) {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
} else {
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfs3_unmount(&lfs3) => 0;
'''
# test the full range of attrs
[cases.test_attrs_all]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
defines.SIZE = 4
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, 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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_setattr(&lfs3, path, a, wbuf, SIZE) => 0;
// try getting the attr size
lfs3_sizeattr(&lfs3, path, a) => SIZE;
// try reading the attr
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// remove the attr
lfs3_removeattr(&lfs3, path, a) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test creating a bunch of attrs
[cases.test_attrs_many]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
defines.M = 40
defines.SIZE = 4
defines.COMPACT = [false, true]
defines.GC_FLAGS = 'LFS3_GC_COMPACT'
defines.GC_STEPS = -1
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
if = 'LFS3_IFDEF_GC(true, !COMPACT)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, 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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_setattr(&lfs3, path, a, wbuf, SIZE) => 0;
}
// try compacting?
#ifdef LFS3_GC
if (COMPACT) {
lfs3_fs_gc(&lfs3) => 0;
}
#endif
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
for (uint16_t a = 0; a < M; a++) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
}
// try reading the attrs
prng = 42;
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.COMPACT = [false, true]
defines.GC_FLAGS = 'LFS3_GC_COMPACT'
defines.GC_STEPS = -1
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
if = 'LFS3_IFDEF_GC(true, !COMPACT)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create N files
uint32_t prng = 42;
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &files[i], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_close(&lfs3, &files[i]) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &files[i],
"snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfs3_mkdir(&lfs3, path) => 0;
}
// create M attrs
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_setattr(&lfs3, path, a, wbuf, SIZE) => 0;
}
}
// try compacting?
#ifdef LFS3_GC
if (COMPACT) {
lfs3_fs_gc(&lfs3) => 0;
}
#endif
if (FILETYPE == 1) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
prng = 42;
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// try getting the attr sizes
for (uint16_t a = 0; a < M; a++) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
}
// try reading the attrs
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
}
lfs3_unmount(&lfs3) => 0;
'''
# fuzz attrs
[cases.test_attrs_fuzz]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
# FILETYPE=3 => root
defines.FILETYPE = [0, 1, 2, 3]
defines.M = 10
defines.SIZE = 4
defines.OPS = '4*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else if (FILETYPE == 2) {
path = "armadillo";
lfs3_mkdir(&lfs3, 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 (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_setattr(&lfs3, 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]) {
lfs3_removeattr(&lfs3, path, a) => 0;
} else {
lfs3_removeattr(&lfs3, path, a) => LFS3_ERR_NOATTR;
}
// update our sim
sim_prngs[a] = 0;
}
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting each attr size
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[a]) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
} else {
lfs3_sizeattr(&lfs3, path, a) => LFS3_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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
}
}
}
// clean up sim/lfs3
free(sim_prngs);
lfs3_unmount(&lfs3) => 0;
'''
# fuzz attrs on multiple files
[cases.test_attrs_fuzz_fuzz]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.OPS = '4*N*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create N files
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[i], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_close(&lfs3, &files[i]) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[i],
"snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfs3_mkdir(&lfs3, 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 (lfs3_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
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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 (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_setattr(&lfs3, path, a, wbuf, SIZE) => 0;
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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]) {
lfs3_removeattr(&lfs3, path, a) => 0;
} else {
lfs3_removeattr(&lfs3, path, a) => LFS3_ERR_NOATTR;
}
// update our sim
sim_prngs[x*M+a] = 0;
}
}
if (FILETYPE == 1) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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]) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
} else {
lfs3_sizeattr(&lfs3, path, a) => LFS3_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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
}
}
}
}
// clean up sim/lfs3
free(sim_prngs);
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// remove the file
lfs3_remove(&lfs3, path) => 0;
// create the file again
// file?
if (FILETYPE == 0) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "miao", strlen("miao"))
=> strlen("miao");
lfs3_file_close(&lfs3, &file_) => 0;
// stickynote?
} else if (FILETYPE == 1) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_,
"bruit d'escargot", strlen("bruit d'escargot"))
=> strlen("bruit d'escargot");
lfs3_file_close(&lfs3, &file_) => 0;
// dir?
} else {
lfs3_mkdir(&lfs3, path) => 0;
}
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'b') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'c') => LFS3_ERR_NOATTR;
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
}
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
lfs3_file_t file;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
path = "snail";
lfs3_file_open(&lfs3, &file, path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "snailnoise", strlen("snailnoise"))
=> strlen("snailnoise");
// create a dir?
} else {
path = "armadillo";
lfs3_mkdir(&lfs3, path) => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, path, 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, path, 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, path, 'c', c, strlen(c)) => 0;
// create another file and move it onto our file
// file?
if (FILETYPE == 0) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_,
"beaver", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "miao", strlen("miao"))
=> strlen("miao");
lfs3_file_close(&lfs3, &file_) => 0;
// stickynote?
} else if (FILETYPE == 1) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_,
"beaver", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_,
"bruit d'escargot", strlen("bruit d'escargot"))
=> strlen("bruit d'escargot");
lfs3_file_close(&lfs3, &file_) => 0;
// dir?
} else {
lfs3_mkdir(&lfs3, "beaver") => 0;
}
lfs3_rename(&lfs3, "beaver", path) => 0;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'b') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, path, 'c') => LFS3_ERR_NOATTR;
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
}
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
defines.REPLACE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
const char *path;
// file?
if (FILETYPE == 0) {
path = "cat";
// stickynote?
} else if (FILETYPE == 0) {
path = "snail";
// dir?
} else {
path = "armadillo";
}
// if replacing create a file to replace
if (REPLACE) {
// from file?
if (FILETYPE == 0) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_,
path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// from stickynote?
} else if (FILETYPE == 1) {
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_,
path, LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "snail noise", strlen("snail noise"))
=> strlen("snail noise");
lfs3_file_close(&lfs3, &file_) => 0;
// from dir?
} else {
lfs3_mkdir(&lfs3, path) => 0;
}
}
// create a file?
lfs3_file_t file;
if (FILETYPE == 0) {
lfs3_file_open(&lfs3, &file, "beaver",
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "miao", strlen("miao"))
=> strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
// stickynote?
} else if (FILETYPE == 1) {
lfs3_file_open(&lfs3, &file,
"beaver", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file,
"bruit d'escargot", strlen("bruit d'escargot"))
=> strlen("bruit d'escargot");
// create a dir?
} else {
lfs3_mkdir(&lfs3, "beaver") => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "beaver", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "beaver", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "beaver", 'c', c, strlen(c)) => 0;
// move file
lfs3_rename(&lfs3, "beaver", path) => 0;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &file) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, path, 'a') => strlen(a);
lfs3_sizeattr(&lfs3, path, 'b') => strlen(b);
lfs3_sizeattr(&lfs3, path, 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfs3_unmount(&lfs3) => 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 => stickynote
# FILETYPE=2 => directory
defines.FILETYPE = [0, 1, 2]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.OPS = '4*N*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create N files
lfs3_file_t files[N];
for (lfs3_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &files[i], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_close(&lfs3, &files[i]) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", i);
lfs3_file_open(&lfs3, &files[i], path,
LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &files[i],
"snail noise", strlen("snail noise"))
=> strlen("snail noise");
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfs3_mkdir(&lfs3, 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 (lfs3_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
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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 (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
lfs3_setattr(&lfs3, path, a, wbuf, SIZE) => 0;
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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]) {
lfs3_removeattr(&lfs3, path, a) => 0;
} else {
lfs3_removeattr(&lfs3, path, a) => LFS3_ERR_NOATTR;
}
// update our sim
sim_prngs[x*M+a] = 0;
// remove a file?
} else if (op == 2) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// remove the file
lfs3_remove(&lfs3, path) => 0;
if (FILETYPE == 1) {
lfs3_file_close(&lfs3, &files[x]) => 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);
lfs3_file_open(&lfs3, &files[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[x], "miao", strlen("miao"))
=> strlen("miao");
lfs3_file_close(&lfs3, &files[x]) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", x);
lfs3_file_open(&lfs3, &files[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[x],
"bruit d'escargot", strlen("bruit d'escargot"))
=> strlen("bruit d'escargot");
// create a dir?
} else {
sprintf(path, "armadillo%03x", x);
lfs3_mkdir(&lfs3, 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
lfs3_size_t x = TEST_PRNG(&prng) % N;
lfs3_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);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", x);
sprintf(path_, "snail%03x", y);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
sprintf(path_, "armadillo%03x", y);
}
// rename the file
lfs3_rename(&lfs3, path, path_) => 0;
if (x != y) {
if (FILETYPE == 1) {
lfs3_file_t wait;
lfs3_file_open(&lfs3, &wait, path_,
LFS3_O_WRONLY) => 0;
lfs3_file_desync(&lfs3, &files[x]) => 0;
lfs3_file_close(&lfs3, &files[x]) => 0;
lfs3_file_close(&lfs3, &files[y]) => 0;
lfs3_file_open(&lfs3, &files[y], path_,
LFS3_O_WRONLY) => 0;
lfs3_file_desync(&lfs3, &wait) => 0;
lfs3_file_close(&lfs3, &wait) => 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);
lfs3_file_open(&lfs3, &files[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[x], "nyan", strlen("nyan"))
=> strlen("nyan");
lfs3_file_close(&lfs3, &files[x]) => 0;
// create a stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%03x", x);
lfs3_file_open(&lfs3, &files[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL) => 0;
lfs3_file_write(&lfs3, &files[x],
"swara keong", strlen("swara keong"))
=> strlen("swara keong");
// create a dir?
} else {
sprintf(path, "armadillo%03x", x);
lfs3_mkdir(&lfs3, 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));
}
}
}
if (FILETYPE == 1) {
for (lfs3_size_t i = 0; i < N; i++) {
lfs3_file_close(&lfs3, &files[i]) => 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// stickynote?
} else if (FILETYPE == 1) {
sprintf(path, "snail%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]) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
} else {
lfs3_sizeattr(&lfs3, path, a) => LFS3_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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf))
=> LFS3_ERR_NOATTR;
}
}
}
}
// clean up sim/lfs3
free(sim_prngs);
lfs3_unmount(&lfs3) => 0;
'''
## Tests involving file-attached attrs
# test that file-attached attrs are read correctly
[cases.test_attrs_fattr_get]
defines.MODE = ['LFS3_A_RDONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with these attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size = -1;
uint8_t b_buf[256];
lfs3_ssize_t b_size = -1;
uint8_t c_buf[256];
lfs3_ssize_t c_size = -1;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that truncate attrs will work
[cases.test_attrs_fattr_trunc]
defines.MODE = ['LFS3_A_RDONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
defines.BUFSIZE = [1, 4, 7]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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] = '!';
lfs3_ssize_t a_size = -1;
uint8_t b_buf[256];
b_buf[BUFSIZE] = '!';
lfs3_ssize_t b_size = -1;
uint8_t c_buf[256];
c_buf[BUFSIZE] = '!';
lfs3_ssize_t c_size = -1;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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] == '!');
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that missing attrs are read correctly
[cases.test_attrs_fattr_noattr]
defines.MODE = ['LFS3_A_RDONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// try opening a file with missing attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size = -1;
uint8_t b_buf[256];
lfs3_ssize_t b_size = -1;
uint8_t c_buf[256];
lfs3_ssize_t c_size = -1;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
// did we read the attrs correctly?
if (MUTSIZE) {
assert(a_size == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(b_size == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(c_size == LFS3_ERR_NOATTR);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# catch LFS3_O_TRUNC mistakes, this has created issues before
[cases.test_attrs_fattr_otrunc]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with these attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size = -1;
uint8_t b_buf[256];
lfs3_ssize_t b_size = -1;
uint8_t c_buf[256];
lfs3_ssize_t c_size = -1;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat",
MODE | LFS3_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);
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# uncreat files should zero attributes
[cases.test_attrs_fattr_uncreat]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// _don't_ create a file
// try opening a file with these attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size = -1;
uint8_t b_buf[256];
lfs3_ssize_t b_size = -1;
uint8_t c_buf[256];
lfs3_ssize_t c_size = -1;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_t file;
lfs3_file_opencfg(&lfs3, &file, "cat",
MODE | LFS3_O_CREAT, &filecfg) => 0;
// did we read the attrs correctly?
if (MUTSIZE) {
assert(a_size == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(b_size == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(c_size == LFS3_ERR_NOATTR);
}
lfs3_file_close(&lfs3, &file) => 0;
lfs3_unmount(&lfs3) => 0;
'''
# test that file-attached attrs are written correctly
[cases.test_attrs_fattr_set]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &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];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can update existing attrs
[cases.test_attrs_fattr_update]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a_);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b_);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c_);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can remove attrs
[cases.test_attrs_fattr_remove]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_attr attrs[] = {
{
.type = 'a',
.flags = MODE,
.buffer = a_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(a_buf)
: LFS3_ERR_NOATTR,
.size = (MUTSIZE) ? &a_size : NULL,
},
{
.type = 'b',
.flags = MODE,
.buffer = b_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(b_buf)
: LFS3_ERR_NOATTR,
.size = (MUTSIZE) ? &b_size : NULL,
},
{
.type = 'c',
.flags = MODE,
.buffer = c_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(c_buf)
: LFS3_ERR_NOATTR,
.size = (MUTSIZE) ? &c_size : NULL,
}
};
struct lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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 = LFS3_ERR_NOATTR;
b_size = LFS3_ERR_NOATTR;
c_size = LFS3_ERR_NOATTR;
// write and close our file to write the attrs out to disk
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, "cat", 'b') => LFS3_ERR_NOATTR;
lfs3_sizeattr(&lfs3, "cat", 'c') => LFS3_ERR_NOATTR;
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => LFS3_ERR_NOATTR;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can set attrs to zero size
#
# this is _not_ the same as removing
#
[cases.test_attrs_fattr_zero]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_attr attrs[] = {
{
.type = 'a',
.flags = MODE,
.buffer = a_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(a_buf)
: 0,
.size = (MUTSIZE) ? &a_size : NULL,
},
{
.type = 'b',
.flags = MODE,
.buffer = b_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(b_buf)
: 0,
.size = (MUTSIZE) ? &b_size : NULL,
},
{
.type = 'c',
.flags = MODE,
.buffer = c_buf,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(c_buf)
: 0,
.size = (MUTSIZE) ? &c_size : NULL,
}
};
struct lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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 zero
a_size = 0;
b_size = 0;
c_size = 0;
// write and close our file to write the attrs out to disk
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => 0;
lfs3_sizeattr(&lfs3, "cat", 'b') => 0;
lfs3_sizeattr(&lfs3, "cat", 'c') => 0;
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => 0;
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => 0;
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can set attrs to zero size + null
#
# this can trip up naive internal logic
#
[cases.test_attrs_fattr_null]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with attrs
uint8_t a_buf[256];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_attr attrs[] = {
{
.type = 'a',
.flags = MODE,
.buffer = (MUTSIZE)
? a_buf
: NULL,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(a_buf)
: 0,
.size = (MUTSIZE) ? &a_size : NULL,
},
{
.type = 'b',
.flags = MODE,
.buffer = (MUTSIZE)
? b_buf
: NULL,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(b_buf)
: 0,
.size = (MUTSIZE) ? &b_size : NULL,
},
{
.type = 'c',
.flags = MODE,
.buffer = (MUTSIZE)
? c_buf
: NULL,
.buffer_size = (MUTSIZE)
? (lfs3_ssize_t)sizeof(c_buf)
: 0,
.size = (MUTSIZE) ? &c_size : NULL,
}
};
struct lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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 zero
a_size = 0;
b_size = 0;
c_size = 0;
// write and close our file to write the attrs out to disk
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => 0;
lfs3_sizeattr(&lfs3, "cat", 'b') => 0;
lfs3_sizeattr(&lfs3, "cat", 'c') => 0;
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => 0;
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => 0;
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that we can set attrs to a size >256
#
# this can trip up internal logic that uses uint8_t sizes
#
[cases.test_attrs_fattr_big]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
defines.SIZE = [500, 509, 512, 513]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
//
// we only make one attr big because otherwise we probably won't
// fit in the mdir
//
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
uint8_t b[1024];
memset(b, 'b', SIZE);
lfs3_setattr(&lfs3, "cat", 'b', b, SIZE) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// try opening a file with attrs
const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips.";
uint8_t b_[1024];
memset(b_, 'B', SIZE);
const char *c_ = "One and two third cups granulated sugar.";
uint8_t a_buf[1024];
lfs3_ssize_t a_size;
uint8_t b_buf[1024];
lfs3_ssize_t b_size;
uint8_t c_buf[1024];
lfs3_ssize_t c_size;
struct lfs3_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) : (lfs3_size_t)SIZE,
.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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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 == SIZE);
assert(memcmp(b_buf, b, SIZE) == 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_, SIZE);
memcpy(c_buf, c_, strlen(c_));
a_size = strlen(a_);
b_size = SIZE;
c_size = strlen(c_);
// write and close our file to write the attrs out to disk
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a_);
lfs3_sizeattr(&lfs3, "cat", 'b') => SIZE;
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c_);
// try reading the attrs
uint8_t rbuf[1024];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, b_, SIZE) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that wronly attrs are not read from disk
[cases.test_attrs_fattr_wronly]
defines.MODE = ['LFS3_A_WRONLY']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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_));
lfs3_ssize_t a_size = strlen(a_);
uint8_t b_buf[256];
memcpy(b_buf, b_, strlen(b_));
lfs3_ssize_t b_size = strlen(b_);
uint8_t c_buf[256];
memcpy(c_buf, c_, strlen(c_));
lfs3_ssize_t c_size = strlen(c_);
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", LFS3_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
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a_);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b_);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c_);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that rdonly attrs have no effect on disk
[cases.test_attrs_fattr_rdonly]
defines.MODE = ['LFS3_A_RDONLY']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", LFS3_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
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attrs are written correctly even if there are no file changes
[cases.test_attrs_fattr_noop]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_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 lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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!
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a_);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b_);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c_);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that lazy attrs aren't written _until_ there are file changes
[cases.test_attrs_fattr_lazy]
defines.MODE = ['LFS3_A_WRONLY', 'LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "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];
lfs3_ssize_t a_size;
uint8_t b_buf[256];
lfs3_ssize_t b_size;
uint8_t c_buf[256];
lfs3_ssize_t c_size;
struct lfs3_attr attrs[] = {
{
.type = 'a',
.flags = MODE | LFS3_A_LAZY,
.buffer = a_buf,
.buffer_size = (MUTSIZE) ? sizeof(a_buf) : strlen(a_),
.size = (MUTSIZE) ? &a_size : NULL,
},
{
.type = 'b',
.flags = MODE | LFS3_A_LAZY,
.buffer = b_buf,
.buffer_size = (MUTSIZE) ? sizeof(b_buf) : strlen(b_),
.size = (MUTSIZE) ? &b_size : NULL,
},
{
.type = 'c',
.flags = MODE | LFS3_A_LAZY,
.buffer = c_buf,
.buffer_size = (MUTSIZE) ? sizeof(c_buf) : strlen(c_),
.size = (MUTSIZE) ? &c_size : NULL,
}
};
struct lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &file, "cat", MODE, &filecfg) => 0;
if (MODE == LFS3_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
lfs3_file_sync(&lfs3, &file) => 0;
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfs3_getattr(&lfs3, "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
lfs3_file_write(&lfs3, &file, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfs3_sizeattr(&lfs3, "cat", 'a') => strlen(a_);
lfs3_sizeattr(&lfs3, "cat", 'b') => strlen(b_);
lfs3_sizeattr(&lfs3, "cat", 'c') => strlen(c_);
// try reading the attrs
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "cat", 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
lfs3_getattr(&lfs3, "cat", 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
lfs3_getattr(&lfs3, "cat", 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attr updates are broadcast to other file handles
[cases.test_attrs_fattr_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_file_write(&lfs3, &file[1], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[1]) => 0;
// were attrs broadcasted to the other files?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attr removes are broadcast to other file handles
[cases.test_attrs_fattr_remove_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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] = LFS3_ERR_NOATTR;
b_size[1] = LFS3_ERR_NOATTR;
c_size[1] = LFS3_ERR_NOATTR;
// write and sync our file to write the attrs out to disk
lfs3_file_write(&lfs3, &file[1], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[1]) => 0;
// were attrs broadcasted to the other files?
for (lfs3_size_t i = 0; i < 3; i++) {
// did we read the attrs correctly?
if (MUTSIZE) {
assert(a_size[i] == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(b_size[i] == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(c_size[i] == LFS3_ERR_NOATTR);
}
}
for (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that setattr can also broadcast
[cases.test_attrs_fattr_setattr_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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.";
lfs3_setattr(&lfs3, "cat", 'a', a_, strlen(a_)) => 0;
const char *b_ = "Three slash four cup butter or margarine.";
lfs3_setattr(&lfs3, "cat", 'b', b_, strlen(b_)) => 0;
const char *c_ = "One and two third cups granulated sugar.";
lfs3_setattr(&lfs3, "cat", 'c', c_, strlen(c_)) => 0;
// were attrs broadcasted to the other files?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attr removes are broadcast to other file handles
[cases.test_attrs_fattr_removeattr_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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 lfs3_removeattr
lfs3_removeattr(&lfs3, "cat", 'a') => 0;
lfs3_removeattr(&lfs3, "cat", 'b') => 0;
lfs3_removeattr(&lfs3, "cat", 'c') => 0;
// were attrs broadcasted to the other files?
for (lfs3_size_t i = 0; i < 3; i++) {
// did we read the attrs correctly?
if (MUTSIZE) {
assert(a_size[i] == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(b_size[i] == LFS3_ERR_NOATTR);
}
if (MUTSIZE) {
assert(c_size[i] == LFS3_ERR_NOATTR);
}
}
for (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attr broadcasts do _not_ update wronly attrs
[cases.test_attrs_fattr_wronly_no_receive]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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 = LFS3_A_WRONLY;
attrs[0][1].flags = LFS3_A_WRONLY;
attrs[0][2].flags = LFS3_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
lfs3_file_write(&lfs3, &file[2], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[2]) => 0;
// were attrs broadcasted to the other files? but not our wronly attrs?
for (lfs3_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.";
lfs3_setattr(&lfs3, "cat", 'a', a__, strlen(a__)) => 0;
const char *b__ = "Dont forget garnishes such as:";
lfs3_setattr(&lfs3, "cat", 'b', b__, strlen(b__)) => 0;
const char *c__ = "Fish-shaped crackers.";
lfs3_setattr(&lfs3, "cat", 'c', c__, strlen(c__)) => 0;
// were attrs broadcasted to the other files? but not our wronly attrs?
for (lfs3_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
lfs3_file_sync(&lfs3, &file[0]) => 0;
// were attrs broadcasted to the other files?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that attr broadcasts do _not_ broadcast rdonly attrs
[cases.test_attrs_fattr_rdonly_no_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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 = LFS3_A_RDONLY;
attrs[0][1].flags = LFS3_A_RDONLY;
attrs[0][2].flags = LFS3_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
lfs3_file_write(&lfs3, &file[0], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[0]) => 0;
// rdonly attrs should not have been broadcasted
for (lfs3_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
lfs3_file_sync(&lfs3, &file[2]) => 0;
// reset attrs?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that desync files do _not_ receive attr broadcasts
[cases.test_attrs_fattr_desync_no_receive]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_file_desync(&lfs3, &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
lfs3_file_write(&lfs3, &file[2], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[2]) => 0;
// were attrs broadcasted to the other files? but not our desync file?
for (lfs3_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.";
lfs3_setattr(&lfs3, "cat", 'a', a__, strlen(a__)) => 0;
const char *b__ = "Dont forget garnishes such as:";
lfs3_setattr(&lfs3, "cat", 'b', b__, strlen(b__)) => 0;
const char *c__ = "Fish-shaped crackers.";
lfs3_setattr(&lfs3, "cat", 'c', c__, strlen(c__)) => 0;
// were attrs broadcasted to the other files? but not our desync file?
for (lfs3_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
lfs3_file_sync(&lfs3, &file[0]) => 0;
// were attrs broadcasted to the other files?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that resync files will reread attrs
[cases.test_attrs_fattr_resync_receive]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_file_desync(&lfs3, &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
lfs3_file_write(&lfs3, &file[2], "miao", strlen("miao")) => strlen("miao");
lfs3_file_sync(&lfs3, &file[2]) => 0;
// were attrs broadcasted to the other files? but not our desync file?
for (lfs3_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
lfs3_file_resync(&lfs3, &file[0]) => 0;
// reread attrs?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that zombie files do _not_ broadcast attrs
[cases.test_attrs_fattr_zombie_no_broadcast]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_remove(&lfs3, "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 the zombie file is a noop
lfs3_file_sync(&lfs3, &file[1]) => 0;
// attempting to resync the zombie file should error
lfs3_file_resync(&lfs3, &file[1]) => LFS3_ERR_NOENT;
// other file unaffected?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test that zombie files do _not_ receive attr broadcasts
[cases.test_attrs_fattr_zombie_no_receive]
defines.MODE = ['LFS3_A_RDWR']
defines.MUTSIZE = [false, true]
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file_;
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file_) => 0;
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfs3_setattr(&lfs3, "cat", 'a', a, strlen(a)) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfs3_setattr(&lfs3, "cat", 'b', b, strlen(b)) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfs3_setattr(&lfs3, "cat", 'c', c, strlen(c)) => 0;
// open a couple files with these attrs
uint8_t a_buf[3][256];
lfs3_ssize_t a_size[3];
uint8_t b_buf[3][256];
lfs3_ssize_t b_size[3];
uint8_t c_buf[3][256];
lfs3_ssize_t c_size[3];
struct lfs3_attr attrs[3][3];
struct lfs3_file_cfg filecfg[3];
lfs3_file_t file[3];
for (lfs3_size_t i = 0; i < 3; i++) {
attrs[i][0] = (struct lfs3_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 lfs3_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 lfs3_attr){
.type = 'c',
.flags = MODE,
.buffer = c_buf[i],
.buffer_size = sizeof(c_buf[i]),
.size = (MUTSIZE) ? &c_size[i] : NULL,
};
filecfg[i] = (struct lfs3_file_cfg){
.attrs = attrs[i],
.attr_count = 3,
};
lfs3_file_opencfg(&lfs3, &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
lfs3_remove(&lfs3, "cat") => 0;
// recreate the file
lfs3_file_open(&lfs3, &file_, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file_, "miao", strlen("miao")) => strlen("miao");
lfs3_file_close(&lfs3, &file_) => 0;
const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips.";
lfs3_setattr(&lfs3, "cat", 'a', a_, strlen(a_)) => 0;
const char *b_ = "Three slash four cup butter or margarine.";
lfs3_setattr(&lfs3, "cat", 'b', b_, strlen(b_)) => 0;
const char *c_ = "One and two third cups granulated sugar.";
lfs3_setattr(&lfs3, "cat", 'c', c_, strlen(c_)) => 0;
// make sure no attrs were broadcasted to our zombies
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
// leave one file a zombie
if (i == 0) {
continue;
}
lfs3_file_close(&lfs3, &file[i]) => 0;
lfs3_file_opencfg(&lfs3, &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
lfs3_file_write(&lfs3, &file[2], "nyan", strlen("nyan")) => strlen("nyan");
lfs3_file_sync(&lfs3, &file[2]) => 0;
// were attrs broadcasted to the other files? but not our zombie file?
for (lfs3_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.";
lfs3_setattr(&lfs3, "cat", 'a', a___, strlen(a___)) => 0;
const char *b___ = "Fish-shaped solid waste.";
lfs3_setattr(&lfs3, "cat", 'b', b___, strlen(b___)) => 0;
const char *c___ = "Fish-shaped dirt.";
lfs3_setattr(&lfs3, "cat", 'c', c___, strlen(c___)) => 0;
// were attrs broadcasted to the other files? but not our zombie file?
for (lfs3_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 is a noop
lfs3_file_sync(&lfs3, &file[0]) => 0;
// attempting to resync the zombie file should error
lfs3_file_resync(&lfs3, &file[0]) => LFS3_ERR_NOENT;
// other file unaffected?
for (lfs3_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 (lfs3_size_t i = 0; i < 3; i++) {
lfs3_file_close(&lfs3, &file[i]) => 0;
}
lfs3_unmount(&lfs3) => 0;
'''
# test the full range of attrs
[cases.test_attrs_fattr_all]
defines.SIZE = 4
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create a file
lfs3_file_t file;
lfs3_file_open(&lfs3, &file, "cat", LFS3_O_WRONLY | LFS3_O_CREAT) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow")) => strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
// setup our attr
uint8_t a_buf[SIZE];
lfs3_ssize_t a_size = -1;
struct lfs3_attr attrs[] = {
{
.flags = LFS3_A_RDWR,
.buffer = a_buf,
.buffer_size = sizeof(a_buf),
.size = &a_size,
}
};
struct lfs3_file_cfg 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 (lfs3_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfs3_file_opencfg(&lfs3, &file, "cat", LFS3_A_RDWR, &filecfg) => 0;
memcpy(a_buf, wbuf, SIZE);
a_size = SIZE;
lfs3_file_close(&lfs3, &file) => 0;
// try getting the attr size
lfs3_sizeattr(&lfs3, "cat", a) => SIZE;
// try reading the attr
uint8_t rbuf[256];
lfs3_getattr(&lfs3, "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));
lfs3_file_opencfg(&lfs3, &file, "cat", LFS3_A_RDWR, &filecfg) => 0;
assert(a_size == SIZE);
assert(memcmp(a_buf, wbuf, SIZE) == 0);
// remove the attr
a_size = LFS3_ERR_NOATTR;
lfs3_file_close(&lfs3, &file) => 0;
// make sure attr is removed
lfs3_sizeattr(&lfs3, "cat", a) => LFS3_ERR_NOATTR;
}
lfs3_unmount(&lfs3) => 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 = 'LFS3_GC_COMPACT'
defines.GC_STEPS = -1
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
if = 'LFS3_IFDEF_GC(true, !COMPACT)'
code = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
// create N files with M attrs
uint32_t prng = 42;
for (lfs3_size_t i = 0; i < N; i++) {
char path[256];
sprintf(path, "cat%03x", i);
uint8_t a_buf[M][SIZE];
struct lfs3_attr attrs[M];
for (lfs3_size_t j = 0; j < M; j++) {
for (lfs3_size_t k = 0; k < SIZE; k++) {
a_buf[j][k] = 'a' + (TEST_PRNG(&prng) % 26);
}
attrs[j] = (struct lfs3_attr){
.type = j,
.flags = LFS3_A_WRONLY,
.buffer = a_buf[j],
.buffer_size = SIZE,
};
}
struct lfs3_file_cfg filecfg = {
.attrs = attrs,
.attr_count = M,
};
lfs3_file_t file;
lfs3_file_opencfg(&lfs3, &file, path,
LFS3_O_WRONLY | LFS3_O_CREAT, &filecfg) => 0;
lfs3_file_write(&lfs3, &file, "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_close(&lfs3, &file) => 0;
}
// try compacting?
#ifdef LFS3_GC
if (COMPACT) {
lfs3_fs_gc(&lfs3) => 0;
}
#endif
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
prng = 42;
for (lfs3_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++) {
lfs3_sizeattr(&lfs3, path, a) => SIZE;
}
// try reading the attrs
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[256];
lfs3_getattr(&lfs3, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
}
lfs3_unmount(&lfs3) => 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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint8_t a_buf[N][M][SIZE];
lfs3_ssize_t a_size[N][M];
struct lfs3_attr attrs[N][M];
struct lfs3_file_cfg filecfg[N];
lfs3_file_t file[N];
// create N files
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
sprintf(path, "cat%03x", x);
for (lfs3_size_t a = 0; a < M; a++) {
attrs[x][a] = (struct lfs3_attr){
.type = a,
.flags = LFS3_A_RDWR,
.buffer = a_buf[x][a],
.buffer_size = SIZE,
.size = &a_size[x][a],
};
}
filecfg[x] = (struct lfs3_file_cfg){
.attrs = attrs[x],
.attr_count = M,
};
lfs3_file_opencfg(&lfs3, &file[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL,
&filecfg[x]) => 0;
lfs3_file_write(&lfs3, &file[x], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_sync(&lfs3, &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 (lfs3_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
lfs3_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 (lfs3_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;
lfs3_file_sync(&lfs3, &file[x]) => 0;
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// remove the attr
a_size[x][a] = LFS3_ERR_NOATTR;
lfs3_file_sync(&lfs3, &file[x]) => 0;
// update our sim
sim_prngs[x*M+a] = 0;
}
}
// check attrs on all file handles
for (lfs3_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 (lfs3_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] == LFS3_ERR_NOATTR);
}
}
}
// clean up sim/lfs3
for (lfs3_size_t x = 0; x < N; x++) {
lfs3_file_close(&lfs3, &file[x]) => 0;
}
free(sim_prngs);
lfs3_unmount(&lfs3) => 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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint8_t a_buf[N][H][M][SIZE];
lfs3_ssize_t a_size[N][H][M];
struct lfs3_attr attrs[N][H][M];
struct lfs3_file_cfg filecfg[N][H];
lfs3_file_t file[N][H];
// create N files
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
sprintf(path, "cat%03x", x);
for (lfs3_size_t y = 0; y < H; y++) {
for (lfs3_size_t a = 0; a < M; a++) {
attrs[x][y][a] = (struct lfs3_attr){
.type = a,
.flags = LFS3_A_RDWR,
.buffer = a_buf[x][y][a],
.buffer_size = SIZE,
.size = &a_size[x][y][a],
};
}
filecfg[x][y] = (struct lfs3_file_cfg){
.attrs = attrs[x][y],
.attr_count = M,
};
lfs3_file_opencfg(&lfs3, &file[x][y], path,
LFS3_O_WRONLY | LFS3_O_CREAT,
&filecfg[x][y]) => 0;
lfs3_file_write(&lfs3, &file[x][y], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_sync(&lfs3, &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 (lfs3_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
lfs3_size_t x = TEST_PRNG(&prng) % N;
// choose a file handle
lfs3_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 (lfs3_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;
lfs3_file_sync(&lfs3, &file[x][y]) => 0;
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
// choose a file handle
lfs3_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] = LFS3_ERR_NOATTR;
lfs3_file_sync(&lfs3, &file[x][y]) => 0;
// update our sim
sim_prngs[x*M+a] = 0;
}
}
// check attrs on all file handles
for (lfs3_size_t x = 0; x < N; x++) {
for (lfs3_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 (lfs3_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] == LFS3_ERR_NOATTR);
}
}
}
}
// clean up sim/lfs3
for (lfs3_size_t x = 0; x < N; x++) {
for (lfs3_size_t y = 0; y < M; y++) {
lfs3_file_close(&lfs3, &file[x][y]) => 0;
}
}
free(sim_prngs);
lfs3_unmount(&lfs3) => 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 = '''
lfs3_t lfs3;
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
uint8_t a_buf[N][M][SIZE];
lfs3_ssize_t a_size[N][M];
struct lfs3_attr attrs[N][M];
struct lfs3_file_cfg filecfg[N];
lfs3_file_t file[N];
// create N files
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
sprintf(path, "cat%03x", x);
for (lfs3_size_t a = 0; a < M; a++) {
attrs[x][a] = (struct lfs3_attr){
.type = a,
.flags = LFS3_A_RDWR,
.buffer = a_buf[x][a],
.buffer_size = SIZE,
.size = &a_size[x][a],
};
}
filecfg[x] = (struct lfs3_file_cfg){
.attrs = attrs[x],
.attr_count = M,
};
lfs3_file_opencfg(&lfs3, &file[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL,
&filecfg[x]) => 0;
lfs3_file_write(&lfs3, &file[x], "meow", strlen("meow"))
=> strlen("meow");
lfs3_file_sync(&lfs3, &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 (lfs3_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
lfs3_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 (lfs3_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;
lfs3_file_sync(&lfs3, &file[x]) => 0;
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// remove the attr
a_size[x][a] = LFS3_ERR_NOATTR;
lfs3_file_sync(&lfs3, &file[x]) => 0;
// update our sim
sim_prngs[x*M+a] = 0;
// remove a file?
} else if (op == 2) {
// choose a file
lfs3_size_t x = TEST_PRNG(&prng) % N;
char path[256];
sprintf(path, "cat%03x", x);
// remove the file
lfs3_remove(&lfs3, 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 (lfs3_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] == LFS3_ERR_NOATTR);
}
}
// but recreate the file so we always have something to
// attach attrs to
lfs3_file_close(&lfs3, &file[x]) => 0;
sprintf(path, "cat%03x", x);
lfs3_file_opencfg(&lfs3, &file[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL,
&filecfg[x]) => 0;
lfs3_file_write(&lfs3, &file[x], "miao", strlen("miao"))
=> strlen("miao");
lfs3_file_sync(&lfs3, &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
lfs3_size_t x = TEST_PRNG(&prng) % N;
lfs3_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
lfs3_rename(&lfs3, 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 (lfs3_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] == LFS3_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 (lfs3_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] == LFS3_ERR_NOATTR);
}
}
if (x != y) {
// but recreate the file so we always have something to
// attach attrs to
lfs3_file_close(&lfs3, &file[y]) => 0;
sprintf(path, "cat%03x", y);
lfs3_file_opencfg(&lfs3, &file[y], path,
LFS3_O_WRONLY,
&filecfg[y]) => 0;
lfs3_file_close(&lfs3, &file[x]) => 0;
sprintf(path, "cat%03x", x);
lfs3_file_opencfg(&lfs3, &file[x], path,
LFS3_O_WRONLY | LFS3_O_CREAT | LFS3_O_EXCL,
&filecfg[x]) => 0;
lfs3_file_write(&lfs3, &file[x], "nyan", strlen("nyan"))
=> strlen("nyan");
lfs3_file_sync(&lfs3, &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 (lfs3_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 (lfs3_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] == LFS3_ERR_NOATTR);
}
}
}
// clean up sim/lfs3
for (lfs3_size_t x = 0; x < N; x++) {
lfs3_file_close(&lfs3, &file[x]) => 0;
}
free(sim_prngs);
lfs3_unmount(&lfs3) => 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
lfs3_t lfs3;
int err = lfs3_mount(&lfs3, LFS3_M_RDWR, CFG);
if (err) {
lfs3_format(&lfs3, LFS3_F_RDWR, CFG) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// keep some test state on disk to survive powerloss
typedef struct fuzz_state {
lfs3_size_t i;
uint32_t prng;
} fuzz_state_t;
lfs3_file_t state_file;
lfs3_file_open(&lfs3, &state_file,
"state", LFS3_O_RDWR | LFS3_O_CREAT) => 0;
fuzz_state_t state;
if (lfs3_file_size(&lfs3, &state_file) == 0) {
state.i = 0;
state.prng = SEED;
} else {
lfs3_file_read(&lfs3, &state_file, &state, sizeof(state))
=> sizeof(state);
}
for (; state.i < OPS; state.i++) {
// choose a random file
lfs3_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];
lfs3_ssize_t a_size[M];
struct lfs3_attr attrs[M];
struct lfs3_file_cfg filecfg;
lfs3_file_t file;
for (lfs3_size_t a = 0; a < M; a++) {
attrs[a] = (struct lfs3_attr){
.type = a,
.flags = LFS3_A_RDWR,
.buffer = a_buf[a],
.buffer_size = SIZE,
.size = &a_size[a],
};
}
filecfg = (struct lfs3_file_cfg){
.attrs = attrs,
.attr_count = M,
};
lfs3_file_opencfg(&lfs3, &file, path, LFS3_O_RDWR | LFS3_O_CREAT,
&filecfg) => 0;
// if the file exists, check our invariant
if (lfs3_file_size(&lfs3, &file) > 0) {
uint8_t wbuf[SIZE];
lfs3_file_read(&lfs3, &file, wbuf, SIZE) => SIZE;
for (lfs3_size_t a = 0; a < M; a++) {
assert(a_size[a] == SIZE);
for (lfs3_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 (lfs3_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng) % 26);
}
lfs3_file_rewind(&lfs3, &file) => 0;
lfs3_file_write(&lfs3, &file, wbuf, SIZE) => SIZE;
// and update attrs
for (lfs3_size_t a = 0; a < M; a++) {
a_size[a] = SIZE;
for (lfs3_size_t j = 0; j < SIZE; j++) {
a_buf[a][j] = 'a' + (((wbuf[j]-'a') + a) % 26);
}
}
// and sync/close
lfs3_file_close(&lfs3, &file) => 0;
// update our state file
lfs3_file_rewind(&lfs3, &state_file) => 0;
lfs3_file_write(&lfs3, &state_file, &state, sizeof(state))
=> sizeof(state);
lfs3_file_sync(&lfs3, &state_file) => 0;
}
// go ahead and close our state file in case we remount
lfs3_file_close(&lfs3, &state_file) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfs3_unmount(&lfs3) => 0;
lfs3_mount(&lfs3, LFS3_M_RDWR, CFG) => 0;
}
// check that our invariant was held in all files
for (lfs3_size_t x = 0; x < N; x++) {
char path[256];
sprintf(path, "cat%03x", x);
uint8_t a_buf[M][SIZE];
lfs3_ssize_t a_size[M];
struct lfs3_attr attrs[M];
struct lfs3_file_cfg filecfg;
lfs3_file_t file;
for (lfs3_size_t a = 0; a < M; a++) {
attrs[a] = (struct lfs3_attr){
.type = a,
.flags = LFS3_A_RDWR,
.buffer = a_buf[a],
.buffer_size = SIZE,
.size = &a_size[a],
};
}
filecfg = (struct lfs3_file_cfg){
.attrs = attrs,
.attr_count = M,
};
int err = lfs3_file_opencfg(&lfs3, &file, path, LFS3_O_RDONLY,
&filecfg);
assert(!err || err == LFS3_ERR_NOENT);
if (err == LFS3_ERR_NOENT) {
continue;
}
// if the file exists, check our invariant
uint8_t wbuf[SIZE];
lfs3_file_read(&lfs3, &file, wbuf, SIZE) => SIZE;
for (lfs3_size_t a = 0; a < M; a++) {
assert(a_size[a] == SIZE);
for (lfs3_size_t j = 0; j < SIZE; j++) {
assert(a_buf[a][j] == 'a' + (((wbuf[j]-'a') + a) % 26));
}
}
lfs3_file_close(&lfs3, &file) => 0;
}
}
lfs3_unmount(&lfs3) => 0;
'''