Compare commits

..

12 Commits

Author SHA1 Message Date
Christopher Haster
013be2665b Fixed lfs_dir_fetchmatch not understanding overwritten tags
Sometimes small, single line code change hides behind it a complicated
story. This is one of those times.

If you look at this diff, you may note that this is a case of
lfs_dir_fetchmatch not correctly handling a tag that invalidates a
callback used to search for some condition, in this case a search for a
parent, which is invalidated by a later dir tag overwritting the
previous dir pair.

But how can this happen? Dir-pair-tags are only overwritten during
relocations (when a block goes bad or exceeds the block_cycles config
option for dynamic wear-leveling). Other dir operations create new
directory entries. And the only lfs_dir_fetchmatch condition that relies
on overwrites (as opposed to proper deletes) is when we need to find a
directory's parent, an operation that only occurs during a _different_
relocation. And a false _positive_, can only happen if we don't have a
parent. Which is really unlikely when we search for directory parents!

This bug and minimal test case was found by Matthew Renzelmann. In a
unfortunate series of events, first a file creation causes a directory
split to occur. This creates a new, orphaned metadata-pair containing
our new file. However, the revision count on this metadata-pair
indicates the pair is due for relocation as a part of wear-leveling.
Normally, this is fine, even though this metadata-pair has no parent,
the lfs_dir_find should return ENOENT and continue without error.
However, here we get hit by our fetchmatch bug. A previous, unrelated
relocation overwrites a pair which just happens to contain the block
allocated for a new metadata-pair. When we search for a parent,
lfs_dir_fetchmatch incorrectly finds this old, outdated metadata pair
and incorrectly tells our orphan it's found its parent.

As you can imagine the orphan's dissapointment must be immense.

So an unfortunately timed dir split triggers a relocation which
incorrectly finds a previously written parent that has been outdated
by another relocation.

As a solution we can outdate our found tag if it is overwritten by
an exact match during lfs_dir_fetchmatch.

As a part of this I started adding a new set of tests: tests/test_relocations,
for aggressive relocations tests. This is already by appended to by
another PR. I suspect relocations is relatively under-tested and is
becoming more important due to recent improvements in wear-leveling.
2019-12-01 16:27:59 -06:00
Christopher Haster
0197b18100 Fixed issue with superblock breaking lfs_dir_seek
The superblock entry takes up id 0 in the root directory (not all
entries are files, though currently the superblock is the only
exception). Normally, reading a directory correctly skips the
superblock and only reports non-superblock files.

However, this doesn't work perfectly for lfs_dir_seek, which tries
to be clever to not touch the disk.

Fortunately, we can fix this by adding an offset for the superblock.
This will only work while the superblock is the only non-file entry,
otherwise we would need to touch the disk to properly seek in a
directory (though we already touch the disk a bit to get dir-tails
during seeks).

Found by jhartika
2019-12-01 16:25:08 -06:00
Christopher Haster
1f11e6b78a Merge pull request #338 from ARMmbed/fix-readme-desc
README: fix incorrect description
2019-12-01 16:24:53 -06:00
Christopher Haster
9a7a3f637a Merge pull request #337 from ARMmbed/fix-null-fetchmatch
fix nullptr access in lfs_dir_fetchmatch (#185)
2019-12-01 16:24:44 -06:00
Christopher Haster
8188019cbf Merge pull request #334 from mon/bugfix/inttypes
Fix some LFS_TRACE format specifiers
2019-12-01 16:22:33 -06:00
Christopher Haster
d6dc728c87 Fixed some issues in lfs_migrate
- Bad size used for writing out softtail tag
- Use of sizeof address instead of intended target
2019-12-01 16:22:15 -06:00
Christopher Haster
aeff2a28cf Stop wear-leveling during migration
Stop proactively relocate blocks during migrations, this can cause a number of
failure states such: clobbering the v1 superblock if we relocate root, and
invalidating directory pointers if we relocate the head of a directory. On top
of this, relocations increase the overall complexity of lfs_migration, which is
already a delicate operation.
2019-12-01 16:21:57 -06:00
Christopher Haster
aae22c8256 Fixed issue with directories falling out of date after block relocation
This is caused by dir->head not being updated when dir->m.pair may be.
This causes the two to fall out of sync and later dir rewinds to fail.

This bug stems all the way back from the first commits of littlefs, so
it's surprising it has avoided detection for this long. Perhaps because
lfs_dir_rewind is not used often.
2019-12-01 16:21:57 -06:00
Christopher Haster
60e67ae080 Fixed implicit change-of-sign warning in lfs_dir_fetch
Warning on MDK v5.27.1
Found by geniusgogo
2019-11-26 16:42:49 -06:00
grunwald-m
64dedee2d1 prepare upstream bugfix of lfs
-> call lfs_dir_fetchmatch with ftag=-1 in order to set the invalid bit
   and never let the function match a dir
2019-11-26 11:48:53 -06:00
Will
5925db48da Fix some LFS_TRACE format specifiers 2019-11-22 14:29:57 +10:00
liaoweixiong
ab56dc5a8b README: fix incorrect description
In my point of view, file updates will commit to filesystem only when
sync or close. There is a extra word 'no' here.

Fixes: bdff4bc59e ("Updated DESIGN.md to reflect v2 changes")
Signed-off-by: liaoweixiong <liaoweixiong@allwinnertech.com>
2019-11-15 18:53:53 +08:00
7 changed files with 292 additions and 53 deletions

View File

@@ -20,6 +20,7 @@ script:
# run tests with a few different configurations
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=8 -DLFS_CACHE_SIZE=16 -DLFS_BLOCK_CYCLES=2"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"

View File

@@ -55,6 +55,7 @@ test: \
test_attrs \
test_move \
test_orphan \
test_relocations \
test_corrupt
@rm test.c
test_%: tests/test_%.sh

View File

@@ -110,7 +110,7 @@ directory functions, with the deviation that the allocation of filesystem
structures must be provided by the user.
All POSIX operations, such as remove and rename, are atomic, even in event
of power-loss. Additionally, no file updates are not actually committed to
of power-loss. Additionally, file updates are not actually committed to
the filesystem until sync or close is called on the file.
## Other notes

118
lfs.c
View File

@@ -466,15 +466,9 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
}
}
// no free blocks in lookahead buffer, need to scan tree
LFS_DEBUG("Scanning for free blocks %"PRIx32"-%"PRIx32,
lfs->free.i + lfs->free.off,
(lfs->free.i + lfs->free.off + 8*lfs->cfg->lookahead_size)
% lfs->cfg->block_count);
// check if we have looked at all blocks since last ack
if (lfs->free.ack == 0) {
LFS_ERROR("No more free space %"PRIx32,
LFS_ERROR("No more free space %"PRIu32,
lfs->free.i + lfs->free.off);
return LFS_ERR_NOSPC;
}
@@ -935,6 +929,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
if (res == LFS_CMP_EQ) {
// found a match
tempbesttag = tag;
} else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) ==
(LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) {
// found an identical tag, but contents didn't match
// this must mean that our besttag has been overwritten
tempbesttag = -1;
} else if (res == LFS_CMP_GT &&
lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) {
// found a greater match, keep track to keep things sorted
@@ -981,9 +980,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
static int lfs_dir_fetch(lfs_t *lfs,
lfs_mdir_t *dir, const lfs_block_t pair[2]) {
// note, mask=-1, tag=0 can never match a tag since this
// note, mask=-1, tag=-1 can never match a tag since this
// pattern has the invalid bit set
return (int)lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL);
return (int)lfs_dir_fetchmatch(lfs, dir, pair,
(lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL);
}
static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
@@ -1383,6 +1383,7 @@ static int lfs_dir_split(lfs_t *lfs,
lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
lfs_mdir_t *source, uint16_t split, uint16_t end) {
// create tail directory
lfs_alloc_ack(lfs);
lfs_mdir_t tail;
int err = lfs_dir_alloc(lfs, &tail);
if (err) {
@@ -1509,9 +1510,13 @@ static int lfs_dir_compact(lfs_t *lfs,
}
}
#ifdef LFS_MIGRATE
} else if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && lfs->lfs1) {
// we can't relocate our root during migrations, as this would
// cause the superblock to get updated, which would clobber v1
} else if (lfs->lfs1) {
// do not proactively relocate blocks during migrations, this
// can cause a number of failure states such: clobbering the
// v1 superblock if we relocate root, and invalidating directory
// pointers if we relocate the head of a directory. On top of
// this, relocations increase the overall complexity of
// lfs_migration, which is already a delicate operation.
#endif
} else {
// we're writing too much, time to relocate
@@ -1649,7 +1654,6 @@ relocate:
if (err && (err != LFS_ERR_NOSPC && !exhausted)) {
return err;
}
continue;
}
@@ -1942,7 +1946,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path);
lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
if (tag < 0) {
LFS_TRACE("lfs_dir_open -> %d", tag);
LFS_TRACE("lfs_dir_open -> %"PRId32, tag);
return tag;
}
@@ -1961,7 +1965,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
if (res < 0) {
LFS_TRACE("lfs_dir_open -> %d", res);
LFS_TRACE("lfs_dir_open -> %"PRId32, res);
return res;
}
lfs_pair_fromle32(pair);
@@ -2070,10 +2074,14 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
dir->pos = lfs_min(2, off);
off -= dir->pos;
while (off != 0) {
dir->id = lfs_min(dir->m.count, off);
dir->pos += dir->id;
off -= dir->id;
// skip superblock entry
dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0);
while (off > 0) {
int diff = lfs_min(dir->m.count - dir->id, off);
dir->id += diff;
dir->pos += diff;
off -= diff;
if (dir->id == dir->m.count) {
if (!dir->m.split) {
@@ -2086,6 +2094,8 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
LFS_TRACE("lfs_dir_seek -> %d", err);
return err;
}
dir->id = 0;
}
}
@@ -2109,8 +2119,6 @@ int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
return err;
}
dir->m.pair[0] = dir->head[0];
dir->m.pair[1] = dir->head[1];
dir->id = 0;
dir->pos = 0;
LFS_TRACE("lfs_dir_rewind -> %d", 0);
@@ -2744,14 +2752,14 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
// flush out any writes
int err = lfs_file_flush(lfs, file);
if (err) {
LFS_TRACE("lfs_file_read -> %"PRId32, err);
LFS_TRACE("lfs_file_read -> %d", err);
return err;
}
}
if (file->pos >= file->ctz.size) {
// eof if past end
LFS_TRACE("lfs_file_read -> %"PRId32, 0);
LFS_TRACE("lfs_file_read -> %d", 0);
return 0;
}
@@ -2767,7 +2775,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
file->ctz.head, file->ctz.size,
file->pos, &file->block, &file->off);
if (err) {
LFS_TRACE("lfs_file_read -> %"PRId32, err);
LFS_TRACE("lfs_file_read -> %d", err);
return err;
}
} else {
@@ -2787,7 +2795,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
file->off, data, diff);
if (err) {
LFS_TRACE("lfs_file_read -> %"PRId32, err);
LFS_TRACE("lfs_file_read -> %d", err);
return err;
}
} else {
@@ -2795,7 +2803,7 @@ lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
NULL, &file->cache, lfs->cfg->block_size,
file->block, file->off, data, diff);
if (err) {
LFS_TRACE("lfs_file_read -> %"PRId32, err);
LFS_TRACE("lfs_file_read -> %d", err);
return err;
}
}
@@ -2824,7 +2832,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
// drop any reads
int err = lfs_file_flush(lfs, file);
if (err) {
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
}
@@ -2835,7 +2843,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
if (file->pos + size > lfs->file_max) {
// Larger than file limit?
LFS_TRACE("lfs_file_write -> %"PRId32, LFS_ERR_FBIG);
LFS_TRACE("lfs_file_write -> %d", LFS_ERR_FBIG);
return LFS_ERR_FBIG;
}
@@ -2861,7 +2869,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
int err = lfs_file_outline(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
}
@@ -2878,7 +2886,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
file->pos-1, &file->block, &file->off);
if (err) {
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
@@ -2893,7 +2901,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
&file->block, &file->off);
if (err) {
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
} else {
@@ -2914,7 +2922,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
goto relocate;
}
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
@@ -2923,7 +2931,7 @@ relocate:
err = lfs_file_relocate(lfs, file);
if (err) {
file->flags |= LFS_F_ERRED;
LFS_TRACE("lfs_file_write -> %"PRId32, err);
LFS_TRACE("lfs_file_write -> %d", err);
return err;
}
}
@@ -2950,7 +2958,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
// write out everything beforehand, may be noop if rdonly
int err = lfs_file_flush(lfs, file);
if (err) {
LFS_TRACE("lfs_file_seek -> %"PRId32, err);
LFS_TRACE("lfs_file_seek -> %d", err);
return err;
}
@@ -2966,7 +2974,7 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
if (npos > lfs->file_max) {
// file position out of range
LFS_TRACE("lfs_file_seek -> %"PRId32, LFS_ERR_INVAL);
LFS_TRACE("lfs_file_seek -> %d", LFS_ERR_INVAL);
return LFS_ERR_INVAL;
}
@@ -3014,7 +3022,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
if (file->pos != oldsize) {
lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
if (res < 0) {
LFS_TRACE("lfs_file_truncate -> %d", res);
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
}
@@ -3023,7 +3031,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
while (file->pos < size) {
lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1);
if (res < 0) {
LFS_TRACE("lfs_file_truncate -> %d", res);
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
}
@@ -3032,7 +3040,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
// restore pos
lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
if (res < 0) {
LFS_TRACE("lfs_file_truncate -> %d", res);
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
@@ -3052,7 +3060,7 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file);
lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET);
if (res < 0) {
LFS_TRACE("lfs_file_rewind -> %d", res);
LFS_TRACE("lfs_file_rewind -> %"PRId32, res);
return (int)res;
}
@@ -3081,7 +3089,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
lfs_mdir_t cwd;
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (tag < 0) {
LFS_TRACE("lfs_stat -> %d", tag);
LFS_TRACE("lfs_stat -> %"PRId32, tag);
return (int)tag;
}
@@ -3102,7 +3110,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
lfs_mdir_t cwd;
lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
if (tag < 0 || lfs_tag_id(tag) == 0x3ff) {
LFS_TRACE("lfs_remove -> %d", (tag < 0) ? tag : LFS_ERR_INVAL);
LFS_TRACE("lfs_remove -> %"PRId32, (tag < 0) ? tag : LFS_ERR_INVAL);
return (tag < 0) ? (int)tag : LFS_ERR_INVAL;
}
@@ -3113,7 +3121,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
if (res < 0) {
LFS_TRACE("lfs_remove -> %d", res);
LFS_TRACE("lfs_remove -> %"PRId32, res);
return (int)res;
}
lfs_pair_fromle32(pair);
@@ -3176,7 +3184,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_mdir_t oldcwd;
lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL);
if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) {
LFS_TRACE("lfs_rename -> %d", (oldtag < 0) ? oldtag : LFS_ERR_INVAL);
LFS_TRACE("lfs_rename -> %"PRId32, (oldtag < 0) ? oldtag : LFS_ERR_INVAL);
return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL;
}
@@ -3186,7 +3194,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) &&
!(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) {
LFS_TRACE("lfs_rename -> %d", (prevtag < 0) ? prevtag : LFS_ERR_INVAL);
LFS_TRACE("lfs_rename -> %"PRId32, (prevtag < 0) ? prevtag : LFS_ERR_INVAL);
return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
}
@@ -3207,7 +3215,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
if (res < 0) {
LFS_TRACE("lfs_rename -> %d", res);
LFS_TRACE("lfs_rename -> %"PRId32, res);
return (int)res;
}
lfs_pair_fromle32(prevpair);
@@ -3302,7 +3310,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
id = 0;
int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
if (err) {
LFS_TRACE("lfs_getattr -> %"PRId32, err);
LFS_TRACE("lfs_getattr -> %d", err);
return err;
}
}
@@ -3313,7 +3321,7 @@ lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
buffer);
if (tag < 0) {
if (tag == LFS_ERR_NOENT) {
LFS_TRACE("lfs_getattr -> %"PRId32, LFS_ERR_NOATTR);
LFS_TRACE("lfs_getattr -> %d", LFS_ERR_NOATTR);
return LFS_ERR_NOATTR;
}
@@ -3769,7 +3777,7 @@ int lfs_fs_traverse(lfs_t *lfs,
if (tag == LFS_ERR_NOENT) {
continue;
}
LFS_TRACE("lfs_fs_traverse -> %d", tag);
LFS_TRACE("lfs_fs_traverse -> %"PRId32, tag);
return tag;
}
lfs_ctz_fromle32(&ctz);
@@ -3893,6 +3901,12 @@ static int lfs_fs_relocate(lfs_t *lfs,
d->m.pair[0] = newpair[0];
d->m.pair[1] = newpair[1];
}
if (d->type == LFS_TYPE_DIR &&
lfs_pair_cmp(oldpair, ((lfs_dir_t*)d)->head) == 0) {
((lfs_dir_t*)d)->head[0] = newpair[0];
((lfs_dir_t*)d)->head[1] = newpair[1];
}
}
// find parent
@@ -4078,11 +4092,11 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs) {
lfs_size_t size = 0;
int err = lfs_fs_traverse(lfs, lfs_fs_size_count, &size);
if (err) {
LFS_TRACE("lfs_fs_size -> %"PRId32, err);
LFS_TRACE("lfs_fs_size -> %d", err);
return err;
}
LFS_TRACE("lfs_fs_size -> %"PRId32, err);
LFS_TRACE("lfs_fs_size -> %d", err);
return size;
}
@@ -4620,7 +4634,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
id, entry1.d.nlen), name},
{LFS_MKTAG(
isdir ? LFS_TYPE_DIRSTRUCT : LFS_TYPE_CTZSTRUCT,
id, sizeof(&entry1.d.u)), &entry1.d.u}));
id, sizeof(entry1.d.u)), &entry1.d.u}));
lfs1_entry_fromle32(&entry1.d);
if (err) {
goto cleanup;
@@ -4643,7 +4657,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_pair_tole32(dir2.pair);
err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
{LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 0),
{LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8),
dir1.d.tail}));
lfs_pair_fromle32(dir2.pair);
if (err) {

View File

@@ -250,6 +250,14 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
## Below, these tests depend _very_ heavily on the geometry of the
## block device being tested, they should be removed and replaced
## by generalized tests. For now we'll just skip if the geometry
## is customized.
if [[ ! $MAKEFLAGS =~ "LFS_BLOCK_CYCLES" ]]
then
echo "--- Chained dir exhaustion test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
@@ -481,4 +489,6 @@ scripts/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
fi
scripts/results.py

139
tests/test_relocations.sh Executable file
View File

@@ -0,0 +1,139 @@
#!/bin/bash
set -eu
export TEST_FILE=$0
trap 'export TEST_LINE=$LINENO' DEBUG
ITERATIONS=20
COUNT=10
echo "=== Relocation tests ==="
rm -rf blocks
scripts/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
// fill up filesystem so only ~16 blocks are left
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file, "padding", LFS_O_CREAT | LFS_O_WRONLY) => 0;
memset(buffer, 0, 512);
while (LFS_BLOCK_COUNT - lfs_fs_size(&lfs) > 16) {
lfs_file_write(&lfs, &file, buffer, 512) => 512;
}
lfs_file_close(&lfs, &file) => 0;
// make a child dir to use in bounded space
lfs_mkdir(&lfs, "child") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dangling split dir test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int j = 0; j < $ITERATIONS; j++) {
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
if (j == $ITERATIONS-1) {
break;
}
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
}
}
lfs_unmount(&lfs) => 0;
TEST
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
echo "--- Outdated head test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int j = 0; j < $ITERATIONS; j++) {
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_open(&lfs, &dir, "child") => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 0;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_file_open(&lfs, &file, path, LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file, "hi", 2) => 2;
lfs_file_close(&lfs, &file) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_rewind(&lfs, &dir) => 0;
lfs_dir_read(&lfs, &dir, &info) => 1;
lfs_dir_read(&lfs, &dir, &info) => 1;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "test%03d_loooooooooooooooooong_name", i);
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(info.name, path) => 0;
info.size => 2;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int i = 0; i < $COUNT; i++) {
sprintf(path, "child/test%03d_loooooooooooooooooong_name", i);
lfs_remove(&lfs, path) => 0;
}
}
lfs_unmount(&lfs) => 0;
TEST
scripts/results.py

View File

@@ -428,4 +428,78 @@ scripts/test.py << TEST
TEST
done
echo "--- Root seek test ---"
./scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int i = 3; i < $MEDIUMSIZE; i++) {
sprintf(path, "hi%03d", i);
lfs_mkdir(&lfs, path) => 0;
}
lfs_dir_open(&lfs, &dir, "/") => 0;
for (int i = 0; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
for (int j = 0; j < $MEDIUMSIZE; j++) {
lfs_soff_t off = -1;
lfs_dir_open(&lfs, &dir, "/") => 0;
for (int i = 0; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
if (i == j) {
off = lfs_dir_tell(&lfs, &dir);
off >= 0 => true;
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
lfs_dir_open(&lfs, &dir, "/") => 0;
lfs_dir_seek(&lfs, &dir, off) => 0;
for (int i = j; i < $MEDIUMSIZE; i++) {
if (i == 0) {
sprintf(path, ".");
} else if (i == 1) {
sprintf(path, "..");
} else if (i == 2) {
sprintf(path, "hello");
} else {
sprintf(path, "hi%03d", i);
}
lfs_dir_read(&lfs, &dir, &info) => 1;
strcmp(path, info.name) => 0;
}
lfs_dir_read(&lfs, &dir, &info) => 0;
lfs_dir_close(&lfs, &dir) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
scripts/results.py