diff --git a/lfs.c b/lfs.c index d0c89f9..3a02348 100644 --- a/lfs.c +++ b/lfs.c @@ -320,17 +320,17 @@ static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { struct lfs_mattr { lfs_tag_t tag; const void *buffer; - const struct lfs_mattr *next; }; -#define LFS_MKATTR(type, id, buffer, size, next) \ - &(const struct lfs_mattr){LFS_MKTAG(type, id, size), (buffer), (next)} - struct lfs_diskoff { lfs_block_t block; lfs_off_t off; }; +#define LFS_MKATTRS(...) \ + (struct lfs_mattr[]){__VA_ARGS__}, \ + sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) + // operations on global state static inline void lfs_gstate_xor(struct lfs_gstate *a, const struct lfs_gstate *b) { @@ -409,7 +409,7 @@ static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { /// Internal operations predeclared here /// static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs); + const struct lfs_mattr *attrs, int attrcount); static int lfs_dir_compact(lfs_t *lfs, lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, lfs_mdir_t *source, uint16_t begin, uint16_t end); @@ -600,13 +600,9 @@ static int lfs_dir_traverse(lfs_t *lfs, buffer = &disk; ptag = tag; } else if (attrcount > 0) { - const struct lfs_mattr *a = attrs; - for (int j = 0; j < attrcount-1; j++) { - a = a->next; - } - - tag = a->tag; - buffer = a->buffer; + tag = attrs[0].tag; + buffer = attrs[0].buffer; + attrs += 1; attrcount -= 1; } else if (!hasseenmove && lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { @@ -647,7 +643,9 @@ static int lfs_dir_traverse(lfs_t *lfs, } // handle special cases for mcu-side operations - if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { + if (lfs_tag_type3(tag) == LFS_FROM_NOOP) { + // do nothing + } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { uint16_t fromid = lfs_tag_size(tag); uint16_t toid = lfs_tag_id(tag); int err = lfs_dir_traverse(lfs, @@ -660,9 +658,10 @@ static int lfs_dir_traverse(lfs_t *lfs, return err; } } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { - for (const struct lfs_attr *a = buffer; a; a = a->next) { - int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a->type, - lfs_tag_id(tag) + diff, a->size), a->buffer); + for (unsigned i = 0; i < lfs_tag_size(tag); i++) { + const struct lfs_attr *a = buffer; + int err = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type, + lfs_tag_id(tag) + diff, a[i].size), a[i].buffer); if (err) { return err; } @@ -1266,9 +1265,8 @@ static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { // steal tail lfs_pair_tole32(tail->tail); - err = lfs_dir_commit(lfs, dir, - LFS_MKATTR(LFS_TYPE_TAIL + tail->split, 0x3ff, tail->tail, 8, - NULL)); + err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); lfs_pair_fromle32(tail->tail); if (err) { return err; @@ -1557,27 +1555,24 @@ relocate: } static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, - const struct lfs_mattr *attrs) { + const struct lfs_mattr *attrs, int attrcount) { // calculate changes to the directory lfs_tag_t deletetag = 0xffffffff; lfs_tag_t createtag = 0xffffffff; - int attrcount = 0; - for (const struct lfs_mattr *a = attrs; a; a = a->next) { - if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) { - createtag = a->tag; + for (int i = 0; i < attrcount; i++) { + if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { + createtag = attrs[i].tag; dir->count += 1; - } else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) { - deletetag = a->tag; + } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) { + deletetag = attrs[i].tag; LFS_ASSERT(dir->count > 0); dir->count -= 1; - } else if (lfs_tag_type1(a->tag) == LFS_TYPE_TAIL) { - dir->tail[0] = ((lfs_block_t*)a->buffer)[0]; - dir->tail[1] = ((lfs_block_t*)a->buffer)[1]; - dir->split = (lfs_tag_chunk(a->tag) & 1); + } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { + dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; + dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; + dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); lfs_pair_fromle32(dir->tail); } - - attrcount += 1; } // do we have a pending move? @@ -1755,9 +1750,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { // setup dir lfs_pair_tole32(pred.tail); - err = lfs_dir_commit(lfs, &dir, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pred.tail, 8, - NULL)); + err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); lfs_pair_fromle32(pred.tail); if (err) { return err; @@ -1768,9 +1762,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { // update tails, this creates a desync lfs_fs_preporphans(lfs, +1); lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &pred, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, - NULL)); + err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); lfs_pair_fromle32(dir.pair); if (err) { return err; @@ -1780,14 +1773,13 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { // now insert into our parent block lfs_pair_tole32(dir.pair); - err = lfs_dir_commit(lfs, &cwd, - LFS_MKATTR(LFS_TYPE_DIRSTRUCT, id, dir.pair, sizeof(dir.pair), - LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen, - LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0, - (!cwd.split) - ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, - NULL) - : NULL)))); + err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, id, 0)}, + {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, + {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, + {!cwd.split + ? LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8) + : LFS_MKTAG(LFS_FROM_NOOP, 0, 0), dir.pair})); lfs_pair_fromle32(dir.pair); if (err) { return err; @@ -2188,11 +2180,10 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, } // get next slot and create entry to remember name - err = lfs_dir_commit(lfs, &file->m, - LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, NULL, 0, - LFS_MKATTR(LFS_TYPE_REG, file->id, path, nlen, - LFS_MKATTR(LFS_TYPE_CREATE, file->id, NULL, 0, - NULL)))); + err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0)}, + {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, + {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0)})); if (err) { err = LFS_ERR_NAMETOOLONG; goto cleanup; @@ -2221,12 +2212,13 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, } // fetch attrs - for (const struct lfs_attr *a = file->cfg->attrs; a; a = a->next) { + for (unsigned i = 0; i < file->cfg->attr_count; i++) { if ((file->flags & 3) != LFS_O_WRONLY) { lfs_stag_t res = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x7ff, 0x3ff, 0), - LFS_MKTAG(LFS_TYPE_USERATTR + a->type, file->id, a->size), - a->buffer); + LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type, + file->id, file->cfg->attrs[i].size), + file->cfg->attrs[i].buffer); if (res < 0 && res != LFS_ERR_NOENT) { err = res; goto cleanup; @@ -2234,7 +2226,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, } if ((file->flags & 3) != LFS_O_RDONLY) { - if (a->size > lfs->attr_max) { + if (file->cfg->attrs[i].size > lfs->attr_max) { err = LFS_ERR_NOSPC; goto cleanup; } @@ -2476,11 +2468,10 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { } // commit file data and attributes - err = lfs_dir_commit(lfs, &file->m, - LFS_MKATTR(LFS_FROM_USERATTRS, - file->id, file->cfg->attrs, 0, - LFS_MKATTR(type, file->id, buffer, size, - NULL))); + err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( + {LFS_MKTAG(type, file->id, size), buffer}, + {LFS_MKTAG(LFS_FROM_USERATTRS, file->id, + file->cfg->attr_count), file->cfg->attrs})); if (err) { if (err == LFS_ERR_NOSPC && (file->flags & LFS_F_INLINE)) { goto relocate; @@ -2850,9 +2841,8 @@ int lfs_remove(lfs_t *lfs, const char *path) { } // delete the entry - err = lfs_dir_commit(lfs, &cwd, - LFS_MKATTR(LFS_TYPE_DELETE, lfs_tag_id(tag), NULL, 0, - NULL)); + err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0)})); if (err) { return err; } @@ -2943,13 +2933,14 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { lfs_fs_prepmove(lfs, newoldtagid, oldcwd.pair); // move over all attributes - err = lfs_dir_commit(lfs, &newcwd, - LFS_MKATTR(LFS_FROM_MOVE, newid, &oldcwd, lfs_tag_id(oldtag), - LFS_MKATTR(lfs_tag_type3(oldtag), newid, newpath, strlen(newpath), - LFS_MKATTR(LFS_TYPE_CREATE, newid, NULL, 0, - (prevtag != LFS_ERR_NOENT) - ? LFS_MKATTR(LFS_TYPE_DELETE, newid, NULL, 0, NULL) - : NULL)))); + err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( + {prevtag != LFS_ERR_NOENT + ? LFS_MKTAG(LFS_TYPE_DELETE, newid, 0) + : LFS_MKTAG(LFS_FROM_NOOP, 0, 0)}, + {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0)}, + {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), + newpath}, + {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd})); if (err) { return err; } @@ -2957,7 +2948,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { // let commit clean up after move (if we're different! otherwise move // logic already fixed it for us) if (lfs_pair_cmp(oldcwd.pair, newcwd.pair) != 0) { - err = lfs_dir_commit(lfs, &oldcwd, NULL); + err = lfs_dir_commit(lfs, &oldcwd, NULL, 0); if (err) { return err; } @@ -3031,9 +3022,8 @@ static int lfs_commitattr(lfs_t *lfs, const char *path, } } - return lfs_dir_commit(lfs, &cwd, - LFS_MKATTR(LFS_TYPE_USERATTR + type, id, buffer, size, - NULL)); + return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer})); } int lfs_setattr(lfs_t *lfs, const char *path, @@ -3198,12 +3188,11 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { }; lfs_superblock_tole32(&superblock); - err = lfs_dir_commit(lfs, &root, - LFS_MKATTR(LFS_TYPE_INLINESTRUCT, 0, - &superblock, sizeof(superblock), - LFS_MKATTR(LFS_TYPE_SUPERBLOCK, 0, "littlefs", 8, - LFS_MKATTR(LFS_TYPE_CREATE, 0, NULL, 0, - NULL)))); + err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0)}, + {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, + {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock})); if (err) { goto cleanup; } @@ -3516,8 +3505,7 @@ static int lfs_fs_relocate(lfs_t *lfs, lfs_fs_preporphans(lfs, +1); lfs_pair_tole32(newpair); - int err = lfs_dir_commit(lfs, &parent, - &(struct lfs_mattr){.tag=tag, .buffer=newpair}); + int err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS({tag, newpair})); lfs_pair_fromle32(newpair); if (err) { return err; @@ -3537,9 +3525,8 @@ static int lfs_fs_relocate(lfs_t *lfs, if (err != LFS_ERR_NOENT) { // replace bad pair, either we clean up desync, or no desync occured lfs_pair_tole32(newpair); - err = lfs_dir_commit(lfs, &parent, - LFS_MKATTR(LFS_TYPE_TAIL + parent.split, 0x3ff, newpair, 8, - NULL)); + err = lfs_dir_commit(lfs, &parent, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); lfs_pair_fromle32(newpair); if (err) { return err; @@ -3600,7 +3587,7 @@ static int lfs_fs_demove(lfs_t *lfs) { } // rely on cancel logic inside commit - err = lfs_dir_commit(lfs, &movedir, NULL); + err = lfs_dir_commit(lfs, &movedir, NULL, 0); if (err) { return err; } @@ -3660,9 +3647,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) { pair[0], pair[1]); lfs_pair_tole32(pair); - err = lfs_dir_commit(lfs, &pdir, - LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pair, 8, - NULL)); + err = lfs_dir_commit(lfs, &pdir, LFS_MKATTRS( + {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pair})); lfs_pair_fromle32(pair); if (err) { return err; diff --git a/lfs.h b/lfs.h index 4dba2b7..61ef66f 100644 --- a/lfs.h +++ b/lfs.h @@ -123,6 +123,7 @@ enum lfs_type { LFS_TYPE_MOVESTATE = 0x7ff, // internal chip sources + LFS_FROM_NOOP = 0x000, LFS_FROM_MOVE = 0x101, LFS_FROM_USERATTRS = 0x102, }; @@ -268,7 +269,8 @@ struct lfs_info { char name[LFS_NAME_MAX+1]; }; -// Custom attribute structure +// Custom attribute structure, used to describe custom attributes +// committed atomically during file writes. struct lfs_attr { // 8-bit type of attribute, provided by user and used to // identify the attribute @@ -279,9 +281,6 @@ struct lfs_attr { // Size of attribute in bytes, limited to LFS_ATTR_MAX lfs_size_t size; - - // Pointer to next attribute in linked list - struct lfs_attr *next; }; // Optional configuration provided during lfs_file_opencfg @@ -290,8 +289,8 @@ struct lfs_file_config { // By default lfs_malloc is used to allocate this buffer. void *buffer; - // Optional linked list of custom attributes related to the file. If the - // file is opened with read access, the attributes will be read from + // Optional list of custom attributes related to the file. If the file + // is opened with read access, these attributes will be read from disk // during the open call. If the file is opened with write access, the // attributes will be written to disk every file sync or close. This // write occurs atomically with update to the file's contents. @@ -302,6 +301,9 @@ struct lfs_file_config { // is larger, then it will be silently truncated. If the attribute is not // found, it will be created implicitly. struct lfs_attr *attrs; + + // Number of custom attributes in the list + lfs_size_t attr_count; }; diff --git a/tests/test_attrs.sh b/tests/test_attrs.sh index e9b2227..e4ff4ce 100755 --- a/tests/test_attrs.sh +++ b/tests/test_attrs.sh @@ -156,10 +156,12 @@ TEST echo "--- Set/get file attribute ---" tests/test.py << TEST lfs_mount(&lfs, &cfg) => 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 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[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0; memcpy(buffer, "aaaa", 4); @@ -173,53 +175,55 @@ tests/test.py << TEST memcmp(buffer+4, "bbbbbb", 6) => 0; memcmp(buffer+10, "ccccc", 5) => 0; - b1.size = 0; + attrs1[1].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; + attrs1[1].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; - b1.size = 6; + attrs1[1].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; + attrs1[1].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; - b1.size = 3; + attrs1[1].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; + attrs1[1].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; - a1.size = LFS_ATTR_MAX+1; + attrs1[0].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}; + 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[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0; memcpy(buffer+4, "fffffffff", 9); lfs_file_close(&lfs, &file[0]) => 0; - a1.size = 4; + attrs1[0].size = 4; lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0; lfs_file_close(&lfs, &file[0]) => 0; @@ -227,10 +231,12 @@ tests/test.py << TEST TEST tests/test.py << TEST lfs_mount(&lfs, &cfg) => 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}; + 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[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0; lfs_file_close(&lfs, &file[0]) => 0; @@ -248,10 +254,12 @@ TEST echo "--- Deferred file attributes ---" tests/test.py << TEST lfs_mount(&lfs, &cfg) => 0; - 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}; + 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[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;