Added support for custom attributes leveraging the new metadata logging

Now that littlefs has been rebuilt almost from the ground up with the
intention to support custom attributes, adding in custom attribute
support is relatively easy.

The highest bit in the 9-bit type structure indicates that an attribute
is a user-specified custom attribute. The user then has a full 8-bits to
specify the attribute type. Other than that, custom attributes are
treated the same as system-level attributes.

Also made some tweaks to custom attributes:
- Adopted the opencfg for file-level attributes provided by dpgeorge
- Changed setattrs/getattrs to the simpler setattr/getattr functions
  users will probably be more familiar with. Note that multiple
  attributes can still be committed atomically with files, though not
  with directories.
- Changed LFS_ATTRS_MAX -> LFS_ATTR_MAX since there's no longer a global
  limit on the sum of attribute sizes, which was rather confusing.
  Though they are still limited by what can fit in a metadata-pair.
This commit is contained in:
Christopher Haster 2018-07-29 15:03:23 -05:00
parent 3914cdf39f
commit 15d156082c
4 changed files with 337 additions and 227 deletions

View File

@ -33,9 +33,9 @@ size: $(OBJ)
$(SIZE) -t $^
.SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate test_entries \
test_interspersed test_alloc test_paths test_attrs \
test_orphan test_move test_corrupt
test: test_format test_dirs test_files test_seek test_truncate \
test_entries test_interspersed test_alloc test_paths test_attrs \
test_move test_orphan test_corrupt
test_%: tests/test_%.sh
ifdef QUIET
@./$< | sed -n '/^[-=]/p'

181
lfs.c
View File

@ -364,7 +364,7 @@ static void lfs_alloc_ack(lfs_t *lfs) {
// d->block_count = lfs_fromle32(d->block_count);
// d->version = lfs_fromle32(d->version);
// d->inline_size = lfs_fromle32(d->inline_size);
// d->attrs_size = lfs_fromle32(d->attrs_size);
// d->attr_size = lfs_fromle32(d->attr_size);
// d->name_size = lfs_fromle32(d->name_size);
//}
//
@ -375,7 +375,7 @@ static void lfs_alloc_ack(lfs_t *lfs) {
// d->block_count = lfs_tole32(d->block_count);
// d->version = lfs_tole32(d->version);
// d->inline_size = lfs_tole32(d->inline_size);
// d->attrs_size = lfs_tole32(d->attrs_size);
// d->attr_size = lfs_tole32(d->attr_size);
// d->name_size = lfs_tole32(d->name_size);
//}
@ -430,7 +430,7 @@ static inline bool lfs_pairsync(
(((uint32_t)(type) << 22) | ((uint32_t)(id) << 12) | (uint32_t)(size))
#define LFS_MKATTR(type, id, buffer, size, next) \
&(const lfs_mattr_t){(next), LFS_MKTAG(type, id, size), (buffer)}
&(const lfs_mattr_t){LFS_MKTAG(type, id, size), (buffer), (next)}
static inline bool lfs_tagisvalid(uint32_t tag) {
return !(tag & 0x80000000);
@ -526,13 +526,20 @@ static int32_t lfs_commitget(lfs_t *lfs, lfs_block_t block, lfs_off_t off,
return LFS_ERR_NOENT;
}
static int lfs_commitattrs(lfs_t *lfs, struct lfs_commit *commit,
uint16_t id, const struct lfs_attr *attrs);
static int lfs_commitmove(lfs_t *lfs, struct lfs_commit *commit,
uint16_t fromid, uint16_t toid,
const lfs_mdir_t *dir, const lfs_mattr_t *attrs);
static int lfs_commitattr(lfs_t *lfs, struct lfs_commit *commit,
uint32_t tag, const void *buffer) {
if (lfs_tagtype(tag) == LFS_FROM_MOVE) {
if (lfs_tagtype(tag) == LFS_FROM_ATTRS) {
// special case for custom attributes
return lfs_commitattrs(lfs, commit,
lfs_tagid(tag), buffer);
} else if (lfs_tagtype(tag) == LFS_FROM_MOVE) {
// special case for moves
return lfs_commitmove(lfs, commit,
lfs_tagsize(tag), lfs_tagid(tag),
@ -586,6 +593,19 @@ static int lfs_commitattr(lfs_t *lfs, struct lfs_commit *commit,
return 0;
}
static int lfs_commitattrs(lfs_t *lfs, struct lfs_commit *commit,
uint16_t id, const struct lfs_attr *attrs) {
for (const struct lfs_attr *a = attrs; a; a = a->next) {
int err = lfs_commitattr(lfs, commit,
LFS_MKTAG(0x100 | a->type, id, a->size), a->buffer);
if (err) {
return err;
}
}
return 0;
}
static int lfs_commitmove(lfs_t *lfs, struct lfs_commit *commit,
uint16_t fromid, uint16_t toid,
const lfs_mdir_t *dir, const lfs_mattr_t *attrs) {
@ -1378,9 +1398,6 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
}
info->type = lfs_tagtype(tag);
if (lfs_tagsize(tag) > lfs->name_size) {
return LFS_ERR_RANGE;
}
struct lfs_ctz ctz;
tag = lfs_dir_get(lfs, dir, 0x7c3ff000,
@ -1410,7 +1427,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
lfs_mdir_t cwd;
int32_t res = lfs_dir_lookup(lfs, &cwd, &path);
if (res != LFS_ERR_NOENT || !path) {
if (!(res == LFS_ERR_NOENT && path)) {
return (res < 0) ? res : LFS_ERR_EXIST;
}
@ -1792,8 +1809,9 @@ static int lfs_ctztraverse(lfs_t *lfs,
/// Top level file operations ///
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_config *cfg) {
// deorphan if we haven't yet, needed at most once after poweron
if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) {
int err = lfs_deorphan(lfs);
@ -1805,7 +1823,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
// allocate entry for file if it doesn't exist
lfs_mdir_t cwd;
int32_t tag = lfs_dir_lookup(lfs, &cwd, &path);
if (tag < 0 && (tag != LFS_ERR_NOENT || !path)) {
if (tag < 0 && !(tag == LFS_ERR_NOENT && path)) {
return tag;
}
@ -1859,6 +1877,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
}
// setup file struct
file->cfg = cfg;
file->pair[0] = cwd.pair[0];
file->pair[1] = cwd.pair[1];
file->id = lfs_tagid(tag);
@ -1866,9 +1885,34 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
file->pos = 0;
file->attrs = NULL;
if (cfg && cfg->attrs) {
// fetch attrs
for (const struct lfs_attr *a = cfg->attrs; a; a = a->next) {
if ((file->flags & 3) != LFS_O_WRONLY) {
int32_t res = lfs_dir_get(lfs, &cwd, 0x7ffff000,
LFS_MKTAG(0x100 | a->type, file->id, a->size), a->buffer);
if (res < 0 && res != LFS_ERR_NOENT) {
return res;
}
}
if ((file->flags & 3) != LFS_O_RDONLY) {
if (a->size > lfs->attr_size) {
return LFS_ERR_NOSPC;
}
file->flags |= LFS_F_DIRTY;
}
}
file->attrs = cfg->attrs;
}
// allocate buffer if needed
file->cache.block = 0xffffffff;
if (lfs->cfg->file_buffer) {
if (file->cfg && file->cfg->buffer) {
file->cache.buffer = file->cfg->buffer;
} else if (lfs->cfg->file_buffer) {
file->cache.buffer = lfs->cfg->file_buffer;
} else if ((file->flags & 3) == LFS_O_RDONLY) {
file->cache.buffer = lfs_malloc(lfs->cfg->read_size);
@ -1909,6 +1953,11 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
return 0;
}
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
return lfs_file_opencfg(lfs, file, path, flags, NULL);
}
int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
int err = lfs_file_sync(lfs, file);
@ -1921,7 +1970,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
}
// clean up memory
if (!lfs->cfg->file_buffer) {
if (!(file->cfg && file->cfg->buffer) && !lfs->cfg->file_buffer) {
lfs_free(file->cache.buffer);
}
@ -2071,7 +2120,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
int err = lfs_dir_commit(lfs, &cwd,
LFS_MKATTR(LFS_TYPE_CTZSTRUCT, file->id,
&file->ctz.head, sizeof(file->ctz),
file->attrs));
LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->attrs, 0,
NULL)));
if (err) {
return err;
}
@ -2079,7 +2129,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
int err = lfs_dir_commit(lfs, &cwd,
LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id,
file->cache.buffer, file->ctz.size,
file->attrs));
LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->attrs, 0,
NULL)));
if (err) {
return err;
}
@ -2688,7 +2739,85 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
return 0;
}
//int lfs_getattrs(lfs_t *lfs, const char *path,
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size) {
lfs_mdir_t cwd;
int32_t res = lfs_dir_lookup(lfs, &cwd, &path);
if (res < 0) {
return res;
}
res = lfs_dir_get(lfs, &cwd, 0x7ffff000,
LFS_MKTAG(0x100 | type, lfs_tagid(res),
lfs_min(size, lfs->attr_size)), buffer);
if (res < 0) {
if (res == LFS_ERR_NOENT) {
return LFS_ERR_NOATTR;
}
return res;
}
return lfs_tagsize(res);
}
int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size) {
if (size > lfs->attr_size) {
return LFS_ERR_NOSPC;
}
lfs_mdir_t cwd;
int32_t res = lfs_dir_lookup(lfs, &cwd, &path);
if (res < 0) {
return res;
}
return lfs_dir_commit(lfs, &cwd,
LFS_MKATTR(0x100 | type, lfs_tagid(res), buffer, size,
NULL));
}
lfs_ssize_t lfs_fs_getattr(lfs_t *lfs,
uint8_t type, void *buffer, lfs_size_t size) {
lfs_mdir_t superdir;
int err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
if (err) {
return err;
}
int32_t res = lfs_dir_get(lfs, &superdir, 0x7ffff000,
LFS_MKTAG(0x100 | type, 0,
lfs_min(size, lfs->attr_size)), buffer);
if (res < 0) {
if (res == LFS_ERR_NOENT) {
return LFS_ERR_NOATTR;
}
return res;
}
return lfs_tagsize(res);
}
int lfs_fs_setattr(lfs_t *lfs,
uint8_t type, const void *buffer, lfs_size_t size) {
if (size > lfs->attr_size) {
return LFS_ERR_NOSPC;
}
lfs_mdir_t superdir;
int err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
if (err) {
return err;
}
return lfs_dir_commit(lfs, &superdir,
LFS_MKATTR(0x100 | type, 0, buffer, size,
NULL));
}
//
//
//
// const struct lfs_attr *attrs, int count) {
// lfs_mdir_t cwd;
// int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
@ -2777,10 +2906,10 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->inline_size = lfs_min(LFS_INLINE_MAX, lfs->cfg->read_size);
}
LFS_ASSERT(lfs->cfg->attrs_size <= LFS_ATTRS_MAX);
lfs->attrs_size = lfs->cfg->attrs_size;
if (!lfs->attrs_size) {
lfs->attrs_size = LFS_ATTRS_MAX;
LFS_ASSERT(lfs->cfg->attr_size <= LFS_ATTR_MAX);
lfs->attr_size = lfs->cfg->attr_size;
if (!lfs->attr_size) {
lfs->attr_size = LFS_ATTR_MAX;
}
LFS_ASSERT(lfs->cfg->name_size <= LFS_NAME_MAX);
@ -2873,7 +3002,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
.block_size = lfs->cfg->block_size,
.block_count = lfs->cfg->block_count,
.inline_size = lfs->inline_size,
.attrs_size = lfs->attrs_size,
.attr_size = lfs->attr_size,
.name_size = lfs->name_size,
};
@ -2954,14 +3083,14 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->inline_size = superblock.inline_size;
}
if (superblock.attrs_size) {
if (superblock.attrs_size > lfs->attrs_size) {
LFS_ERROR("Unsupported attrs size (%d > %d)",
superblock.attrs_size, lfs->attrs_size);
if (superblock.attr_size) {
if (superblock.attr_size > lfs->attr_size) {
LFS_ERROR("Unsupported attr size (%d > %d)",
superblock.attr_size, lfs->attr_size);
return LFS_ERR_INVAL;
}
lfs->attrs_size = superblock.attrs_size;
lfs->attr_size = superblock.attr_size;
}
if (superblock.name_size) {

113
lfs.h
View File

@ -52,18 +52,18 @@ typedef uint32_t lfs_block_t;
// Maximum inline file size in bytes. Large inline files require a larger
// read and prog cache, but if a file can be inline it does not need its own
// data block. LFS_ATTRS_MAX + LFS_INLINE_MAX must be <= 0xffff. Stored in
// data block. LFS_ATTR_MAX + LFS_INLINE_MAX must be <= 0xffff. Stored in
// superblock and must be respected by other littlefs drivers.
#ifndef LFS_INLINE_MAX
#define LFS_INLINE_MAX 0x3ff
#endif
// Maximum size of all attributes per file in bytes, may be redefined but a
// a smaller LFS_ATTRS_MAX has no benefit. LFS_ATTRS_MAX + LFS_INLINE_MAX
// a smaller LFS_ATTR_MAX has no benefit. LFS_ATTR_MAX + LFS_INLINE_MAX
// must be <= 0xffff. Stored in superblock and must be respected by other
// littlefs drivers.
#ifndef LFS_ATTRS_MAX
#define LFS_ATTRS_MAX 0x3f
#ifndef LFS_ATTR_MAX
#define LFS_ATTR_MAX 0x3f
#endif
// Max name size in bytes, may be redefined to reduce the size of the
@ -89,8 +89,7 @@ enum lfs_error {
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NAMETOOLONG = -36, // File name too long
LFS_ERR_NODATA = -61, // No data/attr available
LFS_ERR_RANGE = -34, // Result not representable
LFS_ERR_NOATTR = -61, // No data/attr available
};
// File types
@ -119,6 +118,7 @@ enum lfs_type {
LFS_FROM_REGION = 0x000,
LFS_FROM_DISK = 0x200,
LFS_FROM_MOVE = 0x030,
LFS_FROM_ATTRS = 0x020,
};
// File open flags
@ -223,10 +223,10 @@ struct lfs_config {
lfs_size_t inline_size;
// Optional upper limit on attributes per file in bytes. No downside for
// larger attributes size but must be less than LFS_ATTRS_MAX. Defaults to
// LFS_ATTRS_MAX when zero.Stored in superblock and must be respected by
// larger attributes size but must be less than LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.Stored in superblock and must be respected by
// other littlefs drivers.
lfs_size_t attrs_size;
lfs_size_t attr_size;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
@ -235,7 +235,6 @@ struct lfs_config {
lfs_size_t name_size;
};
// File info structure
struct lfs_info {
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
@ -256,16 +255,30 @@ struct lfs_attr {
// Pointer to buffer containing the attribute
void *buffer;
// Size of attribute in bytes, limited to LFS_ATTRS_MAX
// Size of attribute in bytes, limited to LFS_ATTR_MAX
lfs_size_t size;
// Pointer to next attribute in linked list
const struct lfs_attr *next;
};
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_config {
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
// Optional, custom attributes
// TODO document more
const struct lfs_attr *attrs;
};
/// littlefs data structures ///
typedef struct lfs_mattr {
const struct lfs_mattr *next;
int32_t tag;
const void *buffer;
const struct lfs_mattr *next;
} lfs_mattr_t;
typedef struct lfs_globals {
@ -302,13 +315,13 @@ typedef struct lfs_file {
lfs_size_t size;
} ctz;
const struct lfs_file_config *cfg;
const struct lfs_attr *attrs;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
lfs_mattr_t *attrs;
} lfs_file_t;
typedef struct lfs_dir {
@ -328,7 +341,7 @@ typedef struct lfs_superblock {
lfs_size_t block_count;
lfs_size_t inline_size;
lfs_size_t attrs_size;
lfs_size_t attr_size;
lfs_size_t name_size;
} lfs_superblock_t;
@ -358,7 +371,7 @@ typedef struct lfs {
lfs_globals_t diff;
lfs_size_t inline_size;
lfs_size_t attrs_size;
lfs_size_t attr_size;
lfs_size_t name_size;
} lfs_t;
@ -368,7 +381,8 @@ typedef struct lfs {
// Format a block device with the littlefs
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted.
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
@ -377,7 +391,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config);
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs and config must be allocated while mounted.
// lfs and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
@ -416,9 +431,10 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// smaller than the buffer, it is padded with zeros. It the stored attribute
// is larger than the buffer, LFS_ERR_RANGE is returned.
//
// TODO doc
// Returns a negative error code on failure.
int lfs_getattrs(lfs_t *lfs, const char *path,
const struct lfs_attr *attrs, int count);
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes
//
@ -426,23 +442,38 @@ int lfs_getattrs(lfs_t *lfs, const char *path,
// disk based on their type id. Unspecified attributes are left unmodified.
// Specifying an attribute with zero size deletes the attribute.
//
// TODO doc
// Returns a negative error code on failure.
int lfs_setattrs(lfs_t *lfs, const char *path,
const struct lfs_attr *attrs, int count);
int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size);
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined
// by the flags, which are values from the enum lfs_open_flags
// that are bitwise-ored together.
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags);
// Open a file with extra configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags,
const struct lfs_file_config *config);
// Close a file
//
// Any pending writes are written out to storage as though
@ -503,30 +534,6 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
// Returns the size of the file, or a negative error code on failure.
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
// Get custom attributes attached to a file
//
// Attributes are looked up based on the type id. If the stored attribute is
// smaller than the buffer, it is padded with zeros. It the stored attribute
// is larger than the buffer, LFS_ERR_RANGE is returned.
//
// Returns a negative error code on failure.
int lfs_file_getattrs(lfs_t *lfs, lfs_file_t *file,
const struct lfs_attr *attrs, int count);
// Set custom attributes on a file
//
// The array of attributes will be used to update the attributes stored on
// disk based on their type id. Unspecified attributes are left unmodified.
// Specifying an attribute with zero size deletes the attribute.
//
// Note: Attributes are not written out until a call to lfs_file_sync
// or lfs_file_close and must be allocated until the file is closed or
// lfs_file_setattrs is called with a count of zero.
//
// Returns a negative error code on failure.
int lfs_file_setattrs(lfs_t *lfs, lfs_file_t *file,
const struct lfs_attr *attrs, int count);
/// Directory operations ///
@ -583,8 +590,10 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
// smaller than the buffer, it is padded with zeros. It the stored attribute
// is larger than the buffer, LFS_ERR_RANGE is returned.
//
// TODO doc
// Returns a negative error code on failure.
int lfs_fs_getattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count);
lfs_ssize_t lfs_fs_getattr(lfs_t *lfs,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes on the filesystem
//
@ -594,8 +603,10 @@ int lfs_fs_getattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count);
//
// Note: Filesystem level attributes are not available for wear-leveling
//
// TODO doc
// Returns a negative error code on failure.
int lfs_fs_setattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count);
int lfs_fs_setattr(lfs_t *lfs,
uint8_t type, const void *buffer, lfs_size_t size);
// Finds the current size of the filesystem
//

View File

@ -19,65 +19,53 @@ TEST
echo "--- Set/get attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', "aaaa", 4},
{'B', "bbbbbb", 6},
{'C', "ccccc", 5}}, 3) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
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_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'B', "", 0}}, 1) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 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_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'B', "dddddd", 6}}, 1) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 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_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'B', "eee", 3}}, 1) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 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_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, LFS_ATTRS_MAX+1}}, 1) => LFS_ERR_NOSPC;
lfs_setattrs(&lfs, "hello", (struct lfs_attr[]){
{'B', "fffffffff", 9}}, 1) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => LFS_ERR_RANGE;
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;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_getattrs(&lfs, "hello", (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5}}, 3) => 0;
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;
@ -92,64 +80,52 @@ TEST
echo "--- Set/get fs attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_fs_setattrs(&lfs, (struct lfs_attr[]){
{'A', "aaaa", 4},
{'B', "bbbbbb", 6},
{'C', "ccccc", 5}}, 3) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
lfs_fs_setattr(&lfs, 'A', "aaaa", 4) => 0;
lfs_fs_setattr(&lfs, 'B', "bbbbbb", 6) => 0;
lfs_fs_setattr(&lfs, 'C', "ccccc", 5) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 6;
lfs_fs_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_fs_setattrs(&lfs, (struct lfs_attr[]){
{'B', "", 0}}, 1) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
lfs_fs_setattr(&lfs, 'B', "", 0) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 0;
lfs_fs_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_fs_setattrs(&lfs, (struct lfs_attr[]){
{'B', "dddddd", 6}}, 1) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
lfs_fs_setattr(&lfs, 'B', "dddddd", 6) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 6;
lfs_fs_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_fs_setattrs(&lfs, (struct lfs_attr[]){
{'B', "eee", 3}}, 1) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
lfs_fs_setattr(&lfs, 'B', "eee", 3) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 3;
lfs_fs_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_fs_setattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, LFS_ATTRS_MAX+1}}, 1) => LFS_ERR_NOSPC;
lfs_fs_setattrs(&lfs, (struct lfs_attr[]){
{'B', "fffffffff", 9}}, 1) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => LFS_ERR_RANGE;
lfs_fs_setattr(&lfs, 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_fs_setattr(&lfs, 'B', "fffffffff", 9) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 9;
lfs_fs_getattr(&lfs, 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_fs_getattrs(&lfs, (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5}}, 3) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 9) => 9;
lfs_fs_getattr(&lfs, 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
@ -164,78 +140,84 @@ TEST
echo "--- Set/get file attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_WRONLY) => 0;
struct lfs_attr a1 = {'A', buffer, 4};
struct lfs_attr b1 = {'B', buffer+4, 6, &a1};
struct lfs_attr c1 = {'C', buffer+10, 5, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
struct lfs_attr attr[3];
attr[0] = (struct lfs_attr){'A', "aaaa", 4};
attr[1] = (struct lfs_attr){'B', "bbbbbb", 6};
attr[2] = (struct lfs_attr){'C', "ccccc", 5};
lfs_file_setattrs(&lfs, &file[0], attr, 3) => 0;
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
lfs_file_opencfg(&lfs, &file[0], "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]) => 0;
memset(buffer, 0, 15);
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
attr[0] = (struct lfs_attr){'B', "", 0};
lfs_file_setattrs(&lfs, &file[0], attr, 1) => 0;
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
b1.size = 0;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
attr[0] = (struct lfs_attr){'B', "dddddd", 6};
lfs_file_setattrs(&lfs, &file[0], attr, 1) => 0;
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
attr[0] = (struct lfs_attr){'B', "eee", 3};
lfs_file_setattrs(&lfs, &file[0], attr, 1);
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => 0;
b1.size = 3;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
attr[0] = (struct lfs_attr){'A', buffer, LFS_ATTRS_MAX+1};
lfs_file_setattrs(&lfs, &file[0], attr, 1) => LFS_ERR_NOSPC;
attr[0] = (struct lfs_attr){'B', "fffffffff", 9};
lfs_file_open(&lfs, &file[1], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_setattrs(&lfs, &file[1], attr, 1) => LFS_ERR_BADF;
lfs_file_close(&lfs, &file[1]) => 0;
lfs_file_setattrs(&lfs, &file[0], attr, 1) => 0;
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5}}, 3) => LFS_ERR_RANGE;
a1.size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs_file_close(&lfs, &file[0]) => 0;
a1.size = 4;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5}}, 3) => 0;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
@ -250,36 +232,24 @@ TEST
echo "--- Deferred file attributes ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDWR) => 0;
struct lfs_attr attr[] = {
{'B', "gggg", 4},
{'C', "", 0},
{'D', "hhhh", 4},
};
struct lfs_attr a1 = {'B', "gggg", 4};
struct lfs_attr b1 = {'C', "", 0, &a1};
struct lfs_attr c1 = {'D', "hhhh", 4, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
lfs_file_setattrs(&lfs, &file[0], attr, 3) => 0;
lfs_file_getattrs(&lfs, &file[0], (struct lfs_attr[]){
{'B', buffer, 9},
{'C', buffer+9, 9},
{'D', buffer+18, 9}}, 3) => 0;
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_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_getattrs(&lfs, "hello/hello", (struct lfs_attr[]){
{'B', buffer, 9},
{'C', buffer+9, 9},
{'D', buffer+18, 9}}, 3) => 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]) => 0;
lfs_getattrs(&lfs, "hello/hello", (struct lfs_attr[]){
{'B', buffer, 9},
{'C', buffer+9, 9},
{'D', buffer+18, 9}}, 3) => 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;