Switched to strongly ordered directories

Instead of storing files in an arbitrary order, we now store files in
ascending lexicographical order by filename.

Although a big change, this actually has little impact on how littlefs
works internally. We need to support file insertion, and compare file
names to find our position. But since we already need to scan the entire
directory block, this adds relatively little overhead.

What this does allow, is the potential to add B-tree support in the
future in a backwards compatible manner.

How could you add B-trees to littlefs?
1. Add an optional "child" tag with a pointer that allows you to skip to
   a position in the metadata-pair list that composes the directory
2. When splitting a metadata-pair (sound familiar?), we either insert a
   second child tag in our parent, or we create a new root containing
   the child tags.
3. Each layer needs a bit stored in the tail-pointer to indicate if
   we're going to the next layer. This can be created trivially when we
   create a new root.
4. During lookup we keep two pointers containing the bounds of our
   search. We may need to iterate through multiple metadata-pairs in our
   linked-list, but this gives us a O(log n) lookup cost in a balanced
   tree.
5. During deletion we also delete any children pointers. Note that
   children pointers must come before the actual file entry.

This gives us a B-tree implementation that is compatible with the
current directory layout (assuming the files are ordered). This means
that B-trees could be supported by a host PC and ignored on a small
device. And during power-loss, we never end up with a broken filesystem,
just a less-than-optimal tree.

Note that we don't handle removes, so it's possible for a tree to become
unbalanced. But worst case that's the same as the current linked-list
implementation.

All we need to do now is keep directories ordered. If we decide to drop
B-tree support in the future or the B-tree implementation turns out
inherently flawed, we can just drop the ordered requirement without
breaking compatibility and recover the code cost.
This commit is contained in:
Christopher Haster
2018-10-04 14:49:34 -05:00
parent 7af8b81b81
commit aeca7667b3
6 changed files with 165 additions and 165 deletions

232
lfs.c
View File

@@ -128,11 +128,11 @@ static int lfs_bd_cmp(lfs_t *lfs,
} }
if (dat != data[i]) { if (dat != data[i]) {
return false; return (dat < data[i]) ? 1 : 2;
} }
} }
return true; return 0;
} }
static int lfs_bd_flush(lfs_t *lfs, static int lfs_bd_flush(lfs_t *lfs,
@@ -152,12 +152,8 @@ static int lfs_bd_flush(lfs_t *lfs,
int res = lfs_bd_cmp(lfs, int res = lfs_bd_cmp(lfs,
NULL, rcache, diff, NULL, rcache, diff,
pcache->block, pcache->off, pcache->buffer, diff); pcache->block, pcache->off, pcache->buffer, diff);
if (res < 0) { if (res) {
return res; return (res < 0) ? res : LFS_ERR_CORRUPT;
}
if (!res) {
return LFS_ERR_CORRUPT;
} }
} }
@@ -688,7 +684,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
LFS_ASSERT(temp.count > 0); LFS_ASSERT(temp.count > 0);
temp.count -= 1; temp.count -= 1;
if (lfs_tag_id(tag) == lfs_tag_id(tempfoundtag)) { if (tempfoundtag &&
lfs_tag_id(tag) == lfs_tag_id(tempfoundtag)) {
tempfoundtag = 0; tempfoundtag = 0;
} else if (tempfoundtag && } else if (tempfoundtag &&
lfs_tag_id(tag) < lfs_tag_id(tempfoundtag)) { lfs_tag_id(tag) < lfs_tag_id(tempfoundtag)) {
@@ -724,11 +721,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
return res; return res;
} }
if (res) { if (res && (!tempfoundtag ||
tempfoundtag = tag; lfs_tag_id(res) <= lfs_tag_id(tempfoundtag))) {
tempfoundtag = res;
} }
} else {
tempfoundtag = tag;
} }
} }
} }
@@ -740,12 +736,12 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
// consider what we have good enough // consider what we have good enough
if (dir->off > 0) { if (dir->off > 0) {
// synthetic move // synthetic move
if (lfs->globals.hasmove && if (foundtag &&
lfs->globals.hasmove &&
lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) {
if (lfs->globals.id == lfs_tag_id(foundtag)) { if (lfs->globals.id == lfs_tag_id(foundtag)) {
foundtag = 0; foundtag = 0;
} else if (foundtag && } else if (lfs->globals.id < lfs_tag_id(foundtag)) {
lfs->globals.id < lfs_tag_id(foundtag)) {
foundtag -= LFS_MKTAG(0, 1, 0); foundtag -= LFS_MKTAG(0, 1, 0);
} }
} }
@@ -876,24 +872,6 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
return 0; return 0;
} }
static lfs_stag_t lfs_dir_findmatch(lfs_t *lfs,
lfs_mdir_t *dir, const lfs_block_t pair[2], bool fs,
lfs_tag_t findmask, lfs_tag_t findtag,
int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) {
dir->split = true;
dir->tail[0] = pair[0];
dir->tail[1] = pair[1];
while ((dir->split || fs) && !lfs_pair_isnull(dir->tail)) {
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
findmask, findtag, cb, data);
if (tag) {
return tag;
}
}
return LFS_ERR_NOENT;
}
struct lfs_dir_find_match { struct lfs_dir_find_match {
lfs_t *lfs; lfs_t *lfs;
const void *name; const void *name;
@@ -905,22 +883,39 @@ static int lfs_dir_find_match(void *data,
struct lfs_dir_find_match *name = data; struct lfs_dir_find_match *name = data;
lfs_t *lfs = name->lfs; lfs_t *lfs = name->lfs;
const struct lfs_diskoff *disk = buffer; const struct lfs_diskoff *disk = buffer;
(void)tag;
return lfs_bd_cmp(lfs, lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag));
NULL, &lfs->rcache, name->size, int res = lfs_bd_cmp(lfs,
disk->block, disk->off, name->name, name->size); NULL, &lfs->rcache, diff,
disk->block, disk->off, name->name, diff);
if (res < 0) {
return res;
}
// found match?
if (res == 0 && (name->size == lfs_tag_size(tag) ||
lfs_tag_type(tag) == LFS_TYPE_SUPERBLOCK)) {
return tag;
}
// a greater name found, exit early
if (res == 2 && lfs_tag_type(tag) != LFS_TYPE_SUPERBLOCK) {
return tag | 0x1fff;
}
// no match keep looking
return 0;
} }
static lfs_stag_t lfs_dir_find(lfs_t *lfs, static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
lfs_mdir_t *dir, const char **path) { const char **path, uint16_t *id) {
// we reduce path to a single name if we can find it // we reduce path to a single name if we can find it
const char *name = *path; const char *name = *path;
*path = NULL;
// default to root dir // default to root dir
lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x1ff, 0); lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x1ff, 0);
lfs_block_t pair[2] = {lfs->root[0], lfs->root[1]}; dir->tail[0] = lfs->root[0];
dir->tail[1] = lfs->root[1];
while (true) { while (true) {
nextname: nextname:
@@ -964,10 +959,8 @@ nextname:
return tag; return tag;
} }
// update what we've found if path is only a name // update what we've found so far
if (strchr(name, '/') == NULL) {
*path = name; *path = name;
}
// only continue if we hit a directory // only continue if we hit a directory
if (lfs_tag_type(tag) != LFS_TYPE_DIR) { if (lfs_tag_type(tag) != LFS_TYPE_DIR) {
@@ -977,22 +970,44 @@ nextname:
// grab the entry data // grab the entry data
if (lfs_tag_id(tag) != 0x1ff) { if (lfs_tag_id(tag) != 0x1ff) {
lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c3fe000, lfs_stag_t res = lfs_dir_get(lfs, dir, 0x7c3fe000,
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail);
if (res < 0) { if (res < 0) {
return res; return res;
} }
lfs_pair_fromle32(pair); lfs_pair_fromle32(dir->tail);
} }
// find entry matching name // find entry matching name
tag = lfs_dir_findmatch(lfs, dir, pair, false, 0x7c001fff, while (true) {
LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
0x7c000000, LFS_MKTAG(LFS_TYPE_NAME, 0, namelen),
lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs_dir_find_match, &(struct lfs_dir_find_match){
lfs, name, namelen}); lfs, name, namelen});
if (tag < 0) { if (tag < 0) {
return tag; return tag;
} }
if (id) {
if (strchr(name, '/') != NULL) {
// if path is not only name we're not valid candidate
// for creation
*id = 0x1ff;
} else if (tag) {
*id = lfs_tag_id(tag);
} else {
*id = dir->count;
}
}
if (tag && !lfs_tag_isdelete(tag)) {
break;
}
if (lfs_tag_isdelete(tag) || !dir->split) {
return LFS_ERR_NOENT;
}
}
// to next name // to next name
name += namelen; name += namelen;
} }
@@ -1560,10 +1575,12 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
// calculate new directory size // calculate new directory size
lfs_tag_t deletetag = 0xffffffff; lfs_tag_t deletetag = 0xffffffff;
lfs_tag_t createtag = 0xffffffff;
int attrcount = 0; int attrcount = 0;
for (const struct lfs_mattr *a = attrs; a; a = a->next) { for (const struct lfs_mattr *a = attrs; a; a = a->next) {
if (lfs_tag_subtype(a->tag) == LFS_TYPE_NAME) { if (lfs_tag_subtype(a->tag) == LFS_TYPE_NAME) {
dir->count += 1; dir->count += 1;
createtag = a->tag;
} else if (lfs_tag_subtype(a->tag) == LFS_TYPE_DELETE) { } else if (lfs_tag_subtype(a->tag) == LFS_TYPE_DELETE) {
LFS_ASSERT(dir->count > 0); LFS_ASSERT(dir->count > 0);
dir->count -= 1; dir->count -= 1;
@@ -1685,6 +1702,11 @@ compact:
if (d->type == LFS_TYPE_DIR) { if (d->type == LFS_TYPE_DIR) {
((lfs_dir_t*)d)->pos -= 1; ((lfs_dir_t*)d)->pos -= 1;
} }
} else if (&d->m != dir && d->id >= lfs_tag_id(createtag)) {
d->id += 1;
if (d->type == LFS_TYPE_DIR) {
((lfs_dir_t*)d)->pos += 1;
}
} }
while (d->id >= d->m.count && d->m.split) { while (d->id >= d->m.count && d->m.split) {
@@ -1711,9 +1733,10 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
} }
lfs_mdir_t cwd; lfs_mdir_t cwd;
lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); uint16_t id;
if (!(res == LFS_ERR_NOENT && path)) { err = lfs_dir_find(lfs, &cwd, &path, &id);
return (res < 0) ? res : LFS_ERR_EXIST; if (!(err == LFS_ERR_NOENT && id != 0x1ff)) {
return (err < 0) ? err : LFS_ERR_EXIST;
} }
// check that name fits // check that name fits
@@ -1739,7 +1762,6 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
} }
// get next slot and commit // get next slot and commit
uint16_t id = cwd.count;
cwd.tail[0] = dir.pair[0]; cwd.tail[0] = dir.pair[0];
cwd.tail[1] = dir.pair[1]; cwd.tail[1] = dir.pair[1];
lfs_pair_tole32(dir.pair); lfs_pair_tole32(dir.pair);
@@ -1757,7 +1779,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
} }
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path); lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
if (tag < 0) { if (tag < 0) {
return tag; return tag;
} }
@@ -2114,21 +2136,20 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
} }
// setup simple file details // setup simple file details
int err = 0; int err;
file->cfg = cfg; file->cfg = cfg;
file->flags = flags; file->flags = flags;
file->pos = 0; file->pos = 0;
file->cache.buffer = NULL; file->cache.buffer = NULL;
// allocate entry for file if it doesn't exist // allocate entry for file if it doesn't exist
lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path); lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id);
if (tag < 0 && !(tag == LFS_ERR_NOENT && path)) { if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x1ff)) {
err = tag; err = tag;
goto cleanup; goto cleanup;
} }
// get id, add to list of mdirs to catch update changes // get id, add to list of mdirs to catch update changes
file->id = lfs_tag_id(tag);
file->type = LFS_TYPE_REG; file->type = LFS_TYPE_REG;
file->next = (lfs_file_t*)lfs->mlist; file->next = (lfs_file_t*)lfs->mlist;
lfs->mlist = (struct lfs_mlist*)file; lfs->mlist = (struct lfs_mlist*)file;
@@ -2147,7 +2168,6 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
} }
// get next slot and create entry to remember name // get next slot and create entry to remember name
file->id = file->m.count;
err = lfs_dir_commit(lfs, &file->m, err = lfs_dir_commit(lfs, &file->m,
LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, NULL, 0, LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, NULL, 0,
LFS_MKATTR(LFS_TYPE_REG, file->id, path, nlen, LFS_MKATTR(LFS_TYPE_REG, file->id, path, nlen,
@@ -2755,7 +2775,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
/// General fs operations /// /// General fs operations ///
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
lfs_mdir_t cwd; lfs_mdir_t cwd;
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path); lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (tag < 0) { if (tag < 0) {
return tag; return tag;
} }
@@ -2771,12 +2791,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
lfs_mdir_t cwd; lfs_mdir_t cwd;
err = lfs_dir_fetch(lfs, &cwd, lfs->root); lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (err) {
return err;
}
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path);
if (tag < 0) { if (tag < 0) {
return tag; return tag;
} }
@@ -2840,20 +2855,19 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// find old entry // find old entry
lfs_mdir_t oldcwd; lfs_mdir_t oldcwd;
lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath); lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL);
if (oldtag < 0) { if (oldtag < 0) {
return oldtag; return oldtag;
} }
// find new entry // find new entry
lfs_mdir_t newcwd; lfs_mdir_t newcwd;
lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath); uint16_t newid;
if (prevtag < 0 && prevtag != LFS_ERR_NOENT) { lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
return prevtag; if (prevtag < 0 && !(prevtag == LFS_ERR_NOENT && newid != 0x1ff)) {
return err;
} }
uint16_t newid = lfs_tag_id(prevtag);
lfs_mdir_t prevdir; lfs_mdir_t prevdir;
if (prevtag == LFS_ERR_NOENT) { if (prevtag == LFS_ERR_NOENT) {
// check that name fits // check that name fits
@@ -2861,9 +2875,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
if (nlen > lfs->name_max) { if (nlen > lfs->name_max) {
return LFS_ERR_NAMETOOLONG; return LFS_ERR_NAMETOOLONG;
} }
// get next id
newid = newcwd.count;
} else if (lfs_tag_type(prevtag) != lfs_tag_type(oldtag)) { } else if (lfs_tag_type(prevtag) != lfs_tag_type(oldtag)) {
return LFS_ERR_ISDIR; return LFS_ERR_ISDIR;
} else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) { } else if (lfs_tag_type(prevtag) == LFS_TYPE_DIR) {
@@ -2934,12 +2945,12 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_ssize_t lfs_getattr(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) { uint8_t type, void *buffer, lfs_size_t size) {
lfs_mdir_t cwd; lfs_mdir_t cwd;
lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (res < 0) { if (tag < 0) {
return res; return tag;
} }
uint16_t id = lfs_tag_id(res); uint16_t id = lfs_tag_id(tag);
if (id == 0x1ff) { if (id == 0x1ff) {
// special case for root // special case for root
id = 0; id = 0;
@@ -2949,28 +2960,28 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
} }
} }
res = lfs_dir_get(lfs, &cwd, 0x7fffe000, tag = lfs_dir_get(lfs, &cwd, 0x7fffe000,
LFS_MKTAG(0x100 | type, id, lfs_min(size, lfs->attr_max)), LFS_MKTAG(0x100 | type, id, lfs_min(size, lfs->attr_max)),
buffer); buffer);
if (res < 0) { if (tag < 0) {
if (res == LFS_ERR_NOENT) { if (tag == LFS_ERR_NOENT) {
return LFS_ERR_NOATTR; return LFS_ERR_NOATTR;
} }
return res; return tag;
} }
return lfs_tag_size(res); return lfs_tag_size(tag);
} }
static int lfs_commitattr(lfs_t *lfs, const char *path, static int lfs_commitattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size) { uint8_t type, const void *buffer, lfs_size_t size) {
lfs_mdir_t cwd; lfs_mdir_t cwd;
lfs_stag_t res = lfs_dir_find(lfs, &cwd, &path); lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (res < 0) { if (tag < 0) {
return res; return tag;
} }
uint16_t id = lfs_tag_id(res); uint16_t id = lfs_tag_id(tag);
if (id == 0x1ff) { if (id == 0x1ff) {
// special case for root // special case for root
id = 0; id = 0;
@@ -3167,28 +3178,28 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_mdir_t dir = {.tail = {0, 1}}; lfs_mdir_t dir = {.tail = {0, 1}};
while (!lfs_pair_isnull(dir.tail)) { while (!lfs_pair_isnull(dir.tail)) {
// fetch next block in tail list // fetch next block in tail list
lfs_stag_t res = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7fc00000, lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 0x7fc00000,
LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8),
lfs_dir_find_match, &(struct lfs_dir_find_match){ lfs_dir_find_match, &(struct lfs_dir_find_match){
lfs, "littlefs", 8}); lfs, "littlefs", 8});
if (res < 0) { if (tag < 0) {
err = res; err = tag;
goto cleanup; goto cleanup;
} }
// has superblock? // has superblock?
if (res) { if (tag && !lfs_tag_isdelete(tag)) {
// update root // update root
lfs->root[0] = dir.pair[0]; lfs->root[0] = dir.pair[0];
lfs->root[1] = dir.pair[1]; lfs->root[1] = dir.pair[1];
// grab superblock // grab superblock
lfs_superblock_t superblock; lfs_superblock_t superblock;
res = lfs_dir_get(lfs, &dir, 0x7f800000, tag = lfs_dir_get(lfs, &dir, 0x7f800000,
LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)), LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, sizeof(superblock)),
&superblock); &superblock);
if (res < 0) { if (tag < 0) {
err = res; err = tag;
goto cleanup; goto cleanup;
} }
lfs_superblock_fromle32(&superblock); lfs_superblock_fromle32(&superblock);
@@ -3277,10 +3288,6 @@ int lfs_unmount(lfs_t *lfs) {
/// Filesystem filesystem operations /// /// Filesystem filesystem operations ///
int lfs_fs_traverse(lfs_t *lfs, int lfs_fs_traverse(lfs_t *lfs,
int (*cb)(void *data, lfs_block_t block), void *data) { int (*cb)(void *data, lfs_block_t block), void *data) {
if (lfs_pair_isnull(lfs->root)) {
return 0;
}
// iterate over metadata pairs // iterate over metadata pairs
lfs_mdir_t dir = {.tail = {0, 1}}; lfs_mdir_t dir = {.tail = {0, 1}};
while (!lfs_pair_isnull(dir.tail)) { while (!lfs_pair_isnull(dir.tail)) {
@@ -3347,10 +3354,6 @@ int lfs_fs_traverse(lfs_t *lfs,
static int lfs_fs_pred(lfs_t *lfs, static int lfs_fs_pred(lfs_t *lfs,
const lfs_block_t pair[2], lfs_mdir_t *pdir) { const lfs_block_t pair[2], lfs_mdir_t *pdir) {
if (lfs_pair_isnull(lfs->root)) {
return LFS_ERR_NOENT;
}
// iterate over all directory directory entries // iterate over all directory directory entries
pdir->tail[0] = 0; pdir->tail[0] = 0;
pdir->tail[1] = 1; pdir->tail[1] = 1;
@@ -3389,23 +3392,20 @@ static int lfs_fs_parent_match(void *data,
} }
lfs_pair_fromle32(child); lfs_pair_fromle32(child);
return (lfs_pair_cmp(child, find->pair) == 0); return (lfs_pair_cmp(child, find->pair) == 0) ? tag : 0;
} }
static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
lfs_mdir_t *parent) { lfs_mdir_t *parent) {
if (lfs_pair_isnull(lfs->root)) { // use fetchmatch with callback to find pairs
return LFS_ERR_NOENT; parent->tail[0] = 0;
} parent->tail[1] = 1;
while (!lfs_pair_isnull(parent->tail)) {
// search for both orderings so we can reuse the find function lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
for (int i = 0; i < 2; i++) { 0x7fc01fff, LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
struct lfs_fs_parent_match match = {lfs, {pair[0], pair[1]}}; lfs_fs_parent_match, &(struct lfs_fs_parent_match){
lfs_stag_t tag = lfs_dir_findmatch(lfs, parent, lfs, {pair[0], pair[1]}});
(const lfs_block_t[2]){0, 1}, true, 0x7fc01fff, if (tag) {
LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
lfs_fs_parent_match, &match);
if (tag != LFS_ERR_NOENT) {
return tag; return tag;
} }
} }

2
lfs.h
View File

@@ -92,7 +92,7 @@ enum lfs_type {
LFS_TYPE_DIR = 0x003, LFS_TYPE_DIR = 0x003,
// internally used types // internally used types
LFS_TYPE_USER = 0x100, LFS_TYPE_USERATTR = 0x100,
LFS_TYPE_NAME = 0x000, LFS_TYPE_NAME = 0x000,
LFS_TYPE_DELETE = 0x020, LFS_TYPE_DELETE = 0x020,
LFS_TYPE_STRUCT = 0x040, LFS_TYPE_STRUCT = 0x040,

View File

@@ -43,11 +43,11 @@ tests/test.py << TEST
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "potato") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0; strcmp(info.name, "burito") => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "potato") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -85,10 +85,10 @@ tests/test.py << TEST
strcmp(info.name, "baked") => 0; strcmp(info.name, "baked") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0; strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0; strcmp(info.name, "sweet") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -100,7 +100,7 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "cactus") => 0; lfs_mkdir(&lfs, "cactus") => 0;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i); sprintf((char*)buffer, "cactus/test%03d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0; lfs_mkdir(&lfs, (char*)buffer) => 0;
} }
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -115,7 +115,7 @@ tests/test.py << TEST
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "test%d", i); sprintf((char*)buffer, "test%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
@@ -208,10 +208,10 @@ tests/test.py << TEST
strcmp(info.name, "baked") => 0; strcmp(info.name, "baked") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0; strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0; strcmp(info.name, "sweet") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -241,10 +241,10 @@ tests/test.py << TEST
strcmp(info.name, "baked") => 0; strcmp(info.name, "baked") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0; strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0; strcmp(info.name, "sweet") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -273,10 +273,10 @@ tests/test.py << TEST
strcmp(info.name, "baked") => 0; strcmp(info.name, "baked") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0; strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0; strcmp(info.name, "sweet") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -332,7 +332,7 @@ tests/test.py << TEST
lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i); sprintf((char*)buffer, "cactus/test%03d", i);
lfs_remove(&lfs, (char*)buffer) => 0; lfs_remove(&lfs, (char*)buffer) => 0;
} }
@@ -361,7 +361,7 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "prickly-pear") => 0; lfs_mkdir(&lfs, "prickly-pear") => 0;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%d", i); sprintf((char*)buffer, "prickly-pear/test%03d", i);
lfs_file_open(&lfs, &file[0], (char*)buffer, lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0; LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = 6; size = 6;
@@ -381,7 +381,7 @@ tests/test.py << TEST
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR; info.type => LFS_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "test%d", i); sprintf((char*)buffer, "test%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
@@ -397,7 +397,7 @@ tests/test.py << TEST
lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%d", i); sprintf((char*)buffer, "prickly-pear/test%03d", i);
lfs_remove(&lfs, (char*)buffer) => 0; lfs_remove(&lfs, (char*)buffer) => 0;
} }

View File

@@ -29,7 +29,7 @@ tests/test.py << TEST
TEST TEST
w_test() { w_test() {
tests/test.py << TEST tests/test.py ${4:-} << TEST
size = $1; size = $1;
lfs_size_t chunk = 31; lfs_size_t chunk = 31;
srand(0); srand(0);
@@ -115,21 +115,21 @@ tests/test.py << TEST
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
info.size => strlen("Hello World!\n"); info.size => strlen("Hello World!\n");
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "smallavacado") => 0; strcmp(info.name, "largeavacado") => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
info.size => $SMALLSIZE; info.size => $LARGESIZE;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "mediumavacado") => 0; strcmp(info.name, "mediumavacado") => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
info.size => $MEDIUMSIZE; info.size => $MEDIUMSIZE;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "largeavacado") => 0;
info.type => LFS_TYPE_REG;
info.size => $LARGESIZE;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "noavacado") => 0; strcmp(info.name, "noavacado") => 0;
info.type => LFS_TYPE_REG; info.type => LFS_TYPE_REG;
info.size => 0; info.size => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "smallavacado") => 0;
info.type => LFS_TYPE_REG;
info.size => $SMALLSIZE;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;

View File

@@ -257,10 +257,10 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0; strcmp(info.name, "bonjour") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0; strcmp(info.name, "ohayo") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -303,10 +303,10 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0; strcmp(info.name, "bonjour") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0; strcmp(info.name, "ohayo") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;

View File

@@ -12,7 +12,7 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0; lfs_mkdir(&lfs, "hello") => 0;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "hello/kitty%d", i); sprintf((char*)buffer, "hello/kitty%03d", i);
lfs_file_open(&lfs, &file[0], (char*)buffer, lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
@@ -39,7 +39,7 @@ tests/test.py << TEST
lfs_soff_t pos; lfs_soff_t pos;
int i; int i;
for (i = 0; i < $SMALLSIZE; i++) { for (i = 0; i < $SMALLSIZE; i++) {
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
pos = lfs_dir_tell(&lfs, &dir[0]); pos = lfs_dir_tell(&lfs, &dir[0]);
@@ -47,12 +47,12 @@ tests/test.py << TEST
pos >= 0 => 1; pos >= 0 => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0; lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
lfs_dir_rewind(&lfs, &dir[0]) => 0; lfs_dir_rewind(&lfs, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%d", 0); sprintf((char*)buffer, "kitty%03d", 0);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
@@ -61,7 +61,7 @@ tests/test.py << TEST
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
lfs_dir_seek(&lfs, &dir[0], pos) => 0; lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
@@ -81,7 +81,7 @@ tests/test.py << TEST
lfs_soff_t pos; lfs_soff_t pos;
int i; int i;
for (i = 0; i < $MEDIUMSIZE; i++) { for (i = 0; i < $MEDIUMSIZE; i++) {
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
pos = lfs_dir_tell(&lfs, &dir[0]); pos = lfs_dir_tell(&lfs, &dir[0]);
@@ -89,12 +89,12 @@ tests/test.py << TEST
pos >= 0 => 1; pos >= 0 => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0; lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
lfs_dir_rewind(&lfs, &dir[0]) => 0; lfs_dir_rewind(&lfs, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%d", 0); sprintf((char*)buffer, "kitty%03d", 0);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
@@ -103,7 +103,7 @@ tests/test.py << TEST
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
lfs_dir_seek(&lfs, &dir[0], pos) => 0; lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i); sprintf((char*)buffer, "kitty%03d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0; strcmp(info.name, (char*)buffer) => 0;
@@ -114,7 +114,7 @@ TEST
echo "--- Simple file seek ---" echo "--- Simple file seek ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0;
lfs_soff_t pos; lfs_soff_t pos;
size = strlen("kittycatcat"); size = strlen("kittycatcat");
@@ -163,7 +163,7 @@ TEST
echo "--- Large file seek ---" echo "--- Large file seek ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDONLY) => 0;
lfs_soff_t pos; lfs_soff_t pos;
size = strlen("kittycatcat"); size = strlen("kittycatcat");
@@ -212,7 +212,7 @@ TEST
echo "--- Simple file seek and write ---" echo "--- Simple file seek and write ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0;
lfs_soff_t pos; lfs_soff_t pos;
size = strlen("kittycatcat"); size = strlen("kittycatcat");
@@ -253,7 +253,7 @@ TEST
echo "--- Large file seek and write ---" echo "--- Large file seek and write ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0;
lfs_soff_t pos; lfs_soff_t pos;
size = strlen("kittycatcat"); size = strlen("kittycatcat");
@@ -296,7 +296,7 @@ TEST
echo "--- Boundary seek and write ---" echo "--- Boundary seek and write ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0;
size = strlen("hedgehoghog"); size = strlen("hedgehoghog");
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
@@ -324,7 +324,7 @@ TEST
echo "--- Out-of-bounds seek ---" echo "--- Out-of-bounds seek ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0; lfs_file_open(&lfs, &file[0], "hello/kitty042", LFS_O_RDWR) => 0;
size = strlen("kittycatcat"); size = strlen("kittycatcat");
lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size; lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size;