Files
littlefs/tests/test_attrs.toml
T
Christopher Haster f539d3341c attrs: (Re)implemented lfsr_setattr/getattr/etc
These functions provide simple access to littlefs's custom attributes,
which are small pieces of user-specified metadata that can be attached
to files, dirs, root, etc:

- lfsr_getattr    - Reads an attribute
- lfsr_sizeattr   - Gets the size of an attribute
- lfsr_setattr    - Writes an attribute
- lfsr_removeattr - Removes an attribute

You may notice these functions look quite a bit different from their
previous incarnations. This is because the custom attribute API is
getting an overhaul based on feedback provided by users

The previous API had some real design flaws that interfered with
usability, but now that things have had some time to settle (6 years!),
hopefully most of the pain points are clear.

Notable changes:

- lfsr_getattr's return value is now limited by buffer size.

  The intention of the previous API, where lfsr_getattr always returns
  the attr size, even if it's larger than the buffer, was to allow users
  to find the attr size without an infinitely large buffer.

  In defense of this design, Linux's getxattr does something somewhat
  similar, returning the attr size when the buffer size equals zero.
  Though getxattr does truncate when buffer size is non-zero, which is
  probably safer.

  But, let's be honest, this multipurpose abuse of lfsr_getattr's return
  value is inconsistent with other read functions and potentially
  dangerous for users.

  I think one of the reasons for this API in Linux-land is the limited
  syscall numbers discouraging new functions, but we have no such
  limitation here! We might as well add a dedicated function for
  this: lfsr_sizeattr.

- No more padding with zeros!

  This was a cludge to get around the lack of returned size in custom
  attributes attached to files, but is inconsistent with other read
  functions, so needs to go.

  In general, inconsistencies violate user assumptions, and are usually
  a sign of a bad API.

- lfsr_setattr now takes flags.

  This gives lfsr_setattr more flexiblity in how it operates, and may
  make future extensions easier.

  lfsr_setattr currently supports two flags, which may look a bit
  familiar:

    LFS_A_CREAT     0x04  // Create an attr if it does not exist
    LFS_A_EXCL      0x08  // Fail if an attr already exists

  One long-term idea is to eventually add a simple lfsr_set function to
  make it easier to create small files, so this sort of design overlap
  between lfsr_setattr and lfsr_file_open is hopefully a good thing.

---

Code-wise, these function are really not that bad. Adding functions adds
code, but these are just small wrappers over our internal lookup/commit
functions:

           code          stack
  before: 36556           2608
  after:  37116 (+1.5%)   2608 (+0.0%)

Of course the real cost of custom attributes is how they interact with
open files, a detail which is conveniently missing for now...
2024-08-22 19:49:18 -05:00

1861 lines
59 KiB
TOML

# Custom attribute tests
after = ['test_files', 'test_fsync', 'test_forphans']
# test some simple attr operations
[cases.test_attrs_setattr]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 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 some simple attr operations
[cases.test_attrs_setattr_trunc]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 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[1] = '!';
// try reading truncated attrs
lfsr_getattr(&lfs, path, 'a', rbuf, 1) => 1;
assert(memcmp(rbuf, a, 1) == 0);
assert(rbuf[1] == '!');
lfsr_getattr(&lfs, path, 'b', rbuf, 1) => 1;
assert(memcmp(rbuf, b, 1) == 0);
assert(rbuf[1] == '!');
lfsr_getattr(&lfs, path, 'c', rbuf, 1) => 1;
assert(memcmp(rbuf, c, 1) == 0);
assert(rbuf[1] == '!');
// mark rbuf so we can detect overflow
rbuf[4] = '!';
// try reading truncated attrs
lfsr_getattr(&lfs, path, 'a', rbuf, 4) => 4;
assert(memcmp(rbuf, a, 4) == 0);
assert(rbuf[4] == '!');
lfsr_getattr(&lfs, path, 'b', rbuf, 4) => 4;
assert(memcmp(rbuf, b, 4) == 0);
assert(rbuf[4] == '!');
lfsr_getattr(&lfs, path, 'c', rbuf, 4) => 4;
assert(memcmp(rbuf, c, 4) == 0);
assert(rbuf[4] == '!');
// try reading the full attrs
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfsr_unmount(&lfs) => 0;
'''
# test ENOATTR works
[cases.test_attrs_setattr_noattr]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// try getting the attr sizes
lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR;
// try reading attrs
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
// try setting attrs but without CREAT
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a), 0) => LFS_ERR_NOATTR;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b), 0) => LFS_ERR_NOATTR;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c), 0) => LFS_ERR_NOATTR;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// make sure setattr didn't quietly create attrs
// try getting the attr sizes
lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR;
// try reading attrs
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
}
lfsr_unmount(&lfs) => 0;
'''
# test EEXIST works
[cases.test_attrs_setattr_excl]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 0;
// try to create again with excl, this should fail
const char *a_ = "Four large eggs. One cup semi-sweet chocolate chips.";
lfsr_setattr(&lfs, path, 'a', a_, strlen(a_),
LFS_A_CREAT | LFS_A_EXCL) => LFS_ERR_EXIST;
const char *b_ = "Three slash four cup butter or margarine.";
lfsr_setattr(&lfs, path, 'b', b_, strlen(b_),
LFS_A_CREAT | LFS_A_EXCL) => LFS_ERR_EXIST;
const char *c_ = "One and two third cups granulated sugar.";
lfsr_setattr(&lfs, path, 'c', c_, strlen(b_),
LFS_A_CREAT | LFS_A_EXCL) => LFS_ERR_EXIST;
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 that updating attrs works
[cases.test_attrs_setattr_update]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
# update based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 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) => 0;
}
const char *b_ = "Three slash four cup butter or margarine.";
if (MASK & 0x2) {
lfsr_setattr(&lfs, path, 'b', b_, strlen(b_), 0) => 0;
}
const char *c_ = "One and two third cups granulated sugar.";
if (MASK & 0x4) {
lfsr_setattr(&lfs, path, 'c', c_, strlen(c_), 0) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfsr_sizeattr(&lfs, path, 'a') => strlen(a_);
} else {
lfsr_sizeattr(&lfs, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfsr_sizeattr(&lfs, path, 'b') => strlen(b_);
} else {
lfsr_sizeattr(&lfs, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfsr_sizeattr(&lfs, path, 'c') => strlen(c_);
} else {
lfsr_sizeattr(&lfs, path, 'c') => strlen(c);
}
// try reading attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a_);
assert(memcmp(rbuf, a_, strlen(a_)) == 0);
} else {
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b_);
assert(memcmp(rbuf, b_, strlen(b_)) == 0);
} else {
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c_);
assert(memcmp(rbuf, c_, strlen(c_)) == 0);
} else {
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfsr_unmount(&lfs) => 0;
'''
# test removing attrs
[cases.test_attrs_removeattr]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
# remove based on this mask
defines.MASK = 'range(0x8)'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 0;
// remove some attrs
if (MASK & 0x1) {
lfsr_removeattr(&lfs, path, 'a') => 0;
}
if (MASK & 0x2) {
lfsr_removeattr(&lfs, path, 'b') => 0;
}
if (MASK & 0x4) {
lfsr_removeattr(&lfs, path, 'c') => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
if (MASK & 0x1) {
lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR;
} else {
lfsr_sizeattr(&lfs, path, 'a') => strlen(a);
}
if (MASK & 0x2) {
lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR;
} else {
lfsr_sizeattr(&lfs, path, 'b') => strlen(b);
}
if (MASK & 0x4) {
lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR;
} else {
lfsr_sizeattr(&lfs, path, 'c') => strlen(c);
}
// try reading the full attrs
uint8_t rbuf[256];
if (MASK & 0x1) {
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
} else {
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf))
=> strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
}
if (MASK & 0x2) {
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
} else {
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf))
=> strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
}
if (MASK & 0x4) {
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
} else {
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf))
=> strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
}
lfsr_unmount(&lfs) => 0;
'''
# test the full range of attrs
[cases.test_attrs_all]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
defines.SIZE = 4
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// try creating every attr, this tests 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,
LFS_A_CREAT | LFS_A_EXCL) => 0;
// try getting the attr size
lfsr_sizeattr(&lfs, path, a) => SIZE;
// try reading the attr
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
// remove the attr
lfsr_removeattr(&lfs, path, a) => 0;
}
lfsr_unmount(&lfs) => 0;
'''
# test creating a bunch of attrs
[cases.test_attrs_many]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
defines.M = 40
defines.SIZE = 4
defines.COMPACT = [false, true]
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// create M attrs
uint32_t prng = 42;
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_setattr(&lfs, path, a, wbuf, SIZE,
LFS_A_CREAT | LFS_A_EXCL) => 0;
}
// try compacting?
if (COMPACT) {
lfsr_fs_gc(&lfs, -1, LFS_GC_COMPACT) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
for (uint16_t a = 0; a < M; a++) {
lfsr_sizeattr(&lfs, path, a) => SIZE;
}
// try reading the attrs
prng = 42;
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
lfsr_unmount(&lfs) => 0;
'''
# test creating a bunch of attrs on a bunch of files
[cases.test_attrs_many_many]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.COMPACT = [false, true]
defines.GC_COMPACT_THRESH = 'BLOCK_SIZE/2'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create N files
uint32_t prng = 42;
for (lfs_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow"))
=> strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfsr_mkdir(&lfs, path) => 0;
}
// create M attrs
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
lfsr_setattr(&lfs, path, a, wbuf, SIZE,
LFS_A_CREAT | LFS_A_EXCL) => 0;
}
}
// try compacting?
if (COMPACT) {
lfsr_fs_gc(&lfs, -1, LFS_GC_COMPACT) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
prng = 42;
for (lfs_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// try getting the attr sizes
for (uint16_t a = 0; a < M; a++) {
lfsr_sizeattr(&lfs, path, a) => SIZE;
}
// try reading the attrs
for (uint16_t a = 0; a < M; a++) {
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26);
}
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
}
}
}
lfsr_unmount(&lfs) => 0;
'''
# fuzz attrs
[cases.test_attrs_fuzz]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
# FILETYPE=2 => root
defines.FILETYPE = [0, 1, 2]
defines.M = 10
defines.SIZE = 4
defines.OPS = '4*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else if (FILETYPE == 1) {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
// do nothing for root
} else {
path = "/";
}
// set up a simulation to compare against
uint32_t *sim_prngs = malloc(M*sizeof(uint32_t));
memset(sim_prngs, 0, M*sizeof(uint32_t));
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 2;
// create an attr?
if (op == 0) {
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// choose a prng
uint32_t wprng = TEST_PRNG(&prng);
// create the attr
uint32_t wprng_ = wprng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
if (sim_prngs[a]) {
lfsr_setattr(&lfs, path, a, wbuf, SIZE, 0) => 0;
} else {
lfsr_setattr(&lfs, path, a, wbuf, SIZE,
LFS_A_CREAT | LFS_A_EXCL) => 0;
}
// update our sim
sim_prngs[a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// remove the attr
if (sim_prngs[a]) {
lfsr_removeattr(&lfs, path, a) => 0;
} else {
lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
// update our sim
sim_prngs[a] = 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting each attr size
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[a]) {
lfsr_sizeattr(&lfs, path, a) => SIZE;
} else {
lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
}
// try reading each attr
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[a]) {
uint32_t wprng_ = sim_prngs[a];
uint8_t wbuf[SIZE];
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
}
}
}
// clean up sim/lfs
free(sim_prngs);
lfsr_unmount(&lfs) => 0;
'''
# fuzz attrs on multiple files
[cases.test_attrs_fuzz_fuzz]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.OPS = '4*N*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create N files
for (lfs_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow"))
=> strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfsr_mkdir(&lfs, path) => 0;
}
}
// set up a simulation to compare against
uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t));
memset(sim_prngs, 0, N*M*sizeof(uint32_t));
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 2;
// create an attr?
if (op == 0) {
// choose a file
lfs_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// choose a prng
uint32_t wprng = TEST_PRNG(&prng);
// create the attr
uint32_t wprng_ = wprng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
if (sim_prngs[x*M+a]) {
lfsr_setattr(&lfs, path, a, wbuf, SIZE, 0) => 0;
} else {
lfsr_setattr(&lfs, path, a, wbuf, SIZE,
LFS_A_CREAT | LFS_A_EXCL) => 0;
}
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// remove the attr
if (sim_prngs[x*M+a]) {
lfsr_removeattr(&lfs, path, a) => 0;
} else {
lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
// update our sim
sim_prngs[x*M+a] = 0;
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
for (lfs_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// try getting each attr size
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[x*M+a]) {
lfsr_sizeattr(&lfs, path, a) => SIZE;
} else {
lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
}
// try reading each attr
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[x*M+a]) {
uint32_t wprng_ = sim_prngs[x*M+a];
uint8_t wbuf[SIZE];
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
}
}
}
}
// clean up sim/lfs
free(sim_prngs);
lfsr_unmount(&lfs) => 0;
'''
# test that removing a file removes all attrs
[cases.test_attrs_rm]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 0;
// remove the file
lfsr_remove(&lfs, path) => 0;
// create the file again
// file?
if (FILETYPE == 0) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao");
lfsr_file_close(&lfs, &file) => 0;
// dir?
} else {
lfsr_mkdir(&lfs, path) => 0;
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR;
// try reading the attrs
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
}
lfsr_unmount(&lfs) => 0;
'''
# test that moving onto a file removes all attrs
[cases.test_attrs_mv_dst]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// create a file?
if (FILETYPE == 0) {
path = "cat";
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow")) => strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
path = "armadillo";
lfsr_mkdir(&lfs, path) => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, path, 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, path, 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, path, 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 0;
// create another file and move it onto our file
// file?
if (FILETYPE == 0) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "beaver", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "miao", strlen("miao")) => strlen("miao");
lfsr_file_close(&lfs, &file) => 0;
// dir?
} else {
lfsr_mkdir(&lfs, "beaver") => 0;
}
lfsr_rename(&lfs, "beaver", path) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfsr_sizeattr(&lfs, path, 'a') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'b') => LFS_ERR_NOATTR;
lfsr_sizeattr(&lfs, path, 'c') => LFS_ERR_NOATTR;
// try reading the attrs
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => LFS_ERR_NOATTR;
}
lfsr_unmount(&lfs) => 0;
'''
# test that moves bring over all attrs
[cases.test_attrs_mv_src]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
defines.REPLACE = [false, true]
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
const char *path;
// file?
if (FILETYPE == 0) {
path = "cat";
// dir?
} else {
path = "armadillo";
}
// if replacing create a file to replace
if (REPLACE) {
// create a file?
if (FILETYPE == 0) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path, LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow"))
=> strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
lfsr_mkdir(&lfs, path) => 0;
}
}
// create a file?
if (FILETYPE == 0) {
lfsr_file_t file;
lfsr_file_open(&lfs, &file, "beaver",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "miao", strlen("miao"))
=> strlen("miao");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
lfsr_mkdir(&lfs, "beaver") => 0;
}
// create some attrs
const char *a = "One 18.25 ounce package chocolate cake mix.";
lfsr_setattr(&lfs, "beaver", 'a', a, strlen(a),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *b = "One can prepared coconut pecan frosting.";
lfsr_setattr(&lfs, "beaver", 'b', b, strlen(b),
LFS_A_CREAT | LFS_A_EXCL) => 0;
const char *c = "Three slash four cup vegetable oil.";
lfsr_setattr(&lfs, "beaver", 'c', c, strlen(c),
LFS_A_CREAT | LFS_A_EXCL) => 0;
// move file
lfsr_rename(&lfs, "beaver", path) => 0;
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
// try getting the attr sizes
lfsr_sizeattr(&lfs, path, 'a') => strlen(a);
lfsr_sizeattr(&lfs, path, 'b') => strlen(b);
lfsr_sizeattr(&lfs, path, 'c') => strlen(c);
// try reading the attrs
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, 'a', rbuf, sizeof(rbuf)) => strlen(a);
assert(memcmp(rbuf, a, strlen(a)) == 0);
lfsr_getattr(&lfs, path, 'b', rbuf, sizeof(rbuf)) => strlen(b);
assert(memcmp(rbuf, b, strlen(b)) == 0);
lfsr_getattr(&lfs, path, 'c', rbuf, sizeof(rbuf)) => strlen(c);
assert(memcmp(rbuf, c, strlen(c)) == 0);
}
lfsr_unmount(&lfs) => 0;
'''
# fuzz attrs mixed with file moves and removes
[cases.test_attrs_mvrm_fuzz_fuzz]
# type of file to attach attrs to
# FILETYPE=0 => regular file
# FILETYPE=1 => directory
defines.FILETYPE = [0, 1]
defines.N = 64
defines.M = 4
defines.SIZE = 4
defines.OPS = '4*N*M'
defines.SEED = 'range(20)'
fuzz = 'SEED'
code = '''
lfs_t lfs;
lfsr_format(&lfs, LFS_F_RDWR, CFG) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
// create N files
for (lfs_size_t i = 0; i < N; i++) {
char path[256];
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", i);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "meow", strlen("meow"))
=> strlen("meow");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
sprintf(path, "armadillo%03x", i);
lfsr_mkdir(&lfs, path) => 0;
}
}
// set up a simulation to compare against
uint32_t *sim_prngs = malloc(N*M*sizeof(uint32_t));
memset(sim_prngs, 0, N*M*sizeof(uint32_t));
uint32_t prng = SEED;
for (lfs_size_t i = 0; i < OPS; i++) {
// choose which operation to do
uint8_t op = TEST_PRNG(&prng) % 4;
// create an attr?
if (op == 0) {
// choose a file
lfs_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// choose a prng
uint32_t wprng = TEST_PRNG(&prng);
// create the attr
uint32_t wprng_ = wprng;
uint8_t wbuf[SIZE];
for (lfs_size_t j = 0; j < SIZE; j++) {
wbuf[j] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
if (sim_prngs[x*M+a]) {
lfsr_setattr(&lfs, path, a, wbuf, SIZE, 0) => 0;
} else {
lfsr_setattr(&lfs, path, a, wbuf, SIZE,
LFS_A_CREAT | LFS_A_EXCL) => 0;
}
// update our sim
sim_prngs[x*M+a] = wprng;
// remove an attr?
} else if (op == 1) {
// choose a file
lfs_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// choose an attr
uint8_t a = TEST_PRNG(&prng) % M;
// remove the attr
if (sim_prngs[x*M+a]) {
lfsr_removeattr(&lfs, path, a) => 0;
} else {
lfsr_removeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
// update our sim
sim_prngs[x*M+a] = 0;
// remove a file?
} else if (op == 2) {
// choose a file
lfs_size_t x = TEST_PRNG(&prng) % N;
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// remove the file
lfsr_remove(&lfs, path) => 0;
// but recreate the file so we always have something to
// attach attrs to
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "miao", strlen("miao"))
=> strlen("miao");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
sprintf(path, "armadillo%03x", x);
lfsr_mkdir(&lfs, path) => 0;
}
// update our sim
memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t));
// rename a file?
} else if (op == 3) {
// choose two files
lfs_size_t x = TEST_PRNG(&prng) % N;
lfs_size_t y = TEST_PRNG(&prng) % N;
char path[256];
char path_[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
sprintf(path_, "cat%03x", y);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
sprintf(path_, "armadillo%03x", y);
}
// rename the file
lfsr_rename(&lfs, path, path_) => 0;
if (x != y) {
// but recreate the file so we always have something to
// attach attrs to
// create a file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
lfsr_file_t file;
lfsr_file_open(&lfs, &file, path,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfsr_file_write(&lfs, &file, "nyan", strlen("nyan"))
=> strlen("nyan");
lfsr_file_close(&lfs, &file) => 0;
// create a dir?
} else {
sprintf(path, "armadillo%03x", x);
lfsr_mkdir(&lfs, path) => 0;
}
// update our sim
memcpy(&sim_prngs[y*M], &sim_prngs[x*M], M*sizeof(uint32_t));
memset(&sim_prngs[x*M], 0, M*sizeof(uint32_t));
}
}
}
for (int remount = 0; remount < 2; remount++) {
// remount?
if (remount) {
lfsr_unmount(&lfs) => 0;
lfsr_mount(&lfs, LFS_M_RDWR, CFG) => 0;
}
for (lfs_size_t x = 0; x < N; x++) {
char path[256];
// file?
if (FILETYPE == 0) {
sprintf(path, "cat%03x", x);
// dir?
} else {
sprintf(path, "armadillo%03x", x);
}
// try getting each attr size
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[x*M+a]) {
lfsr_sizeattr(&lfs, path, a) => SIZE;
} else {
lfsr_sizeattr(&lfs, path, a) => LFS_ERR_NOATTR;
}
}
// try reading each attr
for (uint16_t a = 0; a < M; a++) {
if (sim_prngs[x*M+a]) {
uint32_t wprng_ = sim_prngs[x*M+a];
uint8_t wbuf[SIZE];
for (lfs_size_t i = 0; i < SIZE; i++) {
wbuf[i] = 'a' + (TEST_PRNG(&wprng_) % 26);
}
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf)) => SIZE;
assert(memcmp(rbuf, wbuf, SIZE) == 0);
} else {
uint8_t rbuf[256];
lfsr_getattr(&lfs, path, a, rbuf, sizeof(rbuf))
=> LFS_ERR_NOATTR;
}
}
}
}
// clean up sim/lfs
free(sim_prngs);
lfsr_unmount(&lfs) => 0;
'''
#[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;
#'''