Files
littlefs/tests/test_attrs.toml
T
Christopher Haster b5e503ca85 Made lfsr_file_sync a noop if zombied
So now calling lfsr_file_sync on zombied files is a noop:

  // create a file
  lfsr_file_t a;
  lfsr_file_open(&lfs, &a, "a",
          LFS_O_RDWR | LFS_O_CREAT | LFS_O_EXCL) => 0;

  // remove, creating a zombie
  lfsr_remove(&lfs, "a") => 0;

  // sync, this is now a noop (previously LFS_ERR_NOENT)
  lfsr_file_sync(&lfs, &a) => 0;

  // close is also a noop
  lfsr_file_close(&lfs, &a) => 0;

I've been on the fence on this for a while, on one hand erroring
provides more information to the user, on the other hand a noop is less
surprising if the user comes from other systems.

Ended up making this a noop. I figured minimizing surprises is good API
design, and the user can always use lfsr_stat to check if the file still
exists.

This also matches POSIX, and, perhaps more importantly, the current
version of littlefs.

---

Note that lfsr_file_resync still errors with LFS_ERR_NOENT. It's hard to
argue the file "matches the state of disk" otherwise.

Code changes minimal:

           code          stack          ctx
  before: 35784           2440          640
  after:  35780 (-0.0%)   2440 (+0.0%)  640 (+0.0%)
2025-04-25 17:29:34 -05:00

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