Compare commits

...

32 Commits

Author SHA1 Message Date
Christopher Haster
ce2c01f098 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:32:01 -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
Christopher Haster
6b65737715 Merge pull request #308 from roykuper13/readme-example-update-block-cycles
Update readme example code in accordance to the block_cycles change
2019-10-15 10:36:42 -05:00
Christopher Haster
4ebe6030c5 Merge pull request #294 from ARMmbed/fix-max-null-tests
Fixed off-by-one null terminator in tests
2019-10-15 10:36:04 -05:00
Christopher Haster
7ae8d778f1 Merge pull request #299 from sipke/sipke/fix-types-for-16bit-machines-v2
fix types for 16bit machines v2
2019-10-15 10:35:47 -05:00
Roy Kupershmid
4d068a154d Update README example code in accordance to the block_cycles change
An addition to 38a2a8d. When executing the given example in README,
you immediately get an assertion error because block_cycles is initiated to 0.
2019-10-13 20:27:18 +03:00
Sipke Vriend
ba088aa213 lfs_dir_*: Cast error return codes to int.
For correctness, cast the lfs_stag_t variables to int when returning a negative error code.
2019-10-01 15:24:17 +10:00
Sipke Vriend
955b296bcc lfs_file_rewind: Cast error return codes to int.
For correctness, cast the lfs_stag_t variables to int when returning a negative error code.
2019-10-01 14:22:25 +10:00
Sipke Vriend
241dbc6f86 lfs_stat: Cast error return codes to int.
For correctness, cast the lfs_stag_t variables to int when returning a negative error code.
2019-10-01 14:22:01 +10:00
Sipke Vriend
8cca58f1a6 lfs_file_truncate: ensure lfs_file_seek return code is lsf_soff_t and cast error returns
To ensure 16 bit devices do not invalidly truncate lfs_file_write return codes, change
the return variable to be lfs_ssize_t which is the lfs_file_write return code and
cast to int if it is a negative error code.
2019-10-01 14:20:43 +10:00
Sipke Vriend
97f86af4e9 lfs_remove: Cast tag/error return codes to int.
For correctness, cast the lfs_stag_t variables to int when returning a negative error code.
2019-10-01 13:56:51 +10:00
Sipke Vriend
d40302c5e3 lfs_rename: Cast error return codes to int.
For correctness, cast the lfs_stag_t variables to int when returning a negative error code.
2019-10-01 13:51:52 +10:00
Sipke Vriend
0b5a78e2cd Adjust lfs_dir_find return code to ensure 32 bit value.
lfs_dir_find returns either a negative return code or a tag.
For 32 bit machines with int as 32 bits this co-incides, but for smaller
bit processors, we need to ensure a 32 bit value is returned so change
the return type to lfs_stag_t.
2019-10-01 11:52:02 +10:00
Christopher Haster
27b6cc829b Fixed off-by-one null terminator in tests
Found by mr-at-eo
2019-09-23 10:43:39 -05:00
Christopher Haster
fd204ac2fb Merge pull request #278 from roykuper13/validate-lfs-cfg-sizes
lfs: Validate lfs-cfg sizes before performing any arithmetic logics with them
2019-09-19 10:02:54 -05:00
Christopher Haster
bd99402d9a Merge pull request #281 from patrick--/fix-lfs-embud-file-resource-leak
Fix for issue #260
2019-09-19 10:02:42 -05:00
Christopher Haster
bce442a86b Merge pull request #282 from runderwo/master
Corrections for typos and grammar
2019-09-19 10:02:34 -05:00
Christopher Haster
f26e970a0e Merge pull request #286 from sipke/sipke/fix-warnings-shift-count
build: Fix warnings about shift count width difference for 16 bit com…
2019-09-19 10:02:25 -05:00
Sipke Vriend
965d29b887 build: Fix warnings about shift count width difference for 16 bit compiler
Build warnings exist on a gcc based 16 bit compiler. Cast relevant types
to fix.

littlefs/lfs.c: In function 'lfs_gstate_xororphans':
littlefs/lfs.c:355:5: warning: left shift count >= width of type
littlefs/lfs.c: In function 'lfs_dir_fetchmatch':
littlefs/lfs.c:849:17: warning: left shift count >= width of type
littlefs/lfs.c: In function 'lfs_dir_commitcrc':
littlefs/lfs.c:1278:9: warning: left shift count >= width of type
2019-09-09 13:53:50 +10:00
Ryan Underwood
f7fd7d966a Corrections for typos and grammar 2019-09-01 21:11:49 -07:00
Patrick Servello
d5aba27d60 Fix for issue #260
Certain functions within lfs_emubd.c were susceptible to file resource leaks due to certain code paths not issuing an fclose() before returning.
2019-08-31 20:47:26 -05:00
Roy Kupershmid
0c77123eee lfs: Validate lfs-cfg sizes before performing arithmetic logics with them 2019-08-31 16:57:56 +03:00
10 changed files with 358 additions and 95 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

@@ -254,7 +254,7 @@ have weaknesses that limit their usefulness. But if we merge the two they can
mutually solve each other's limitations.
This is the idea behind littlefs. At the sub-block level, littlefs is built
out of small, two blocks logs that provide atomic updates to metadata anywhere
out of small, two block logs that provide atomic updates to metadata anywhere
on the filesystem. At the super-block level, littlefs is a CObW tree of blocks
that can be evicted on demand.
@@ -676,7 +676,7 @@ block, this cost is fairly reasonable.
---
This is a new data structure, so we still have several questions. What is the
storage overage? Can the number of pointers exceed the size of a block? How do
storage overhead? Can the number of pointers exceed the size of a block? How do
we store a CTZ skip-list in our metadata pairs?
To find the storage overhead, we can look at the data structure as multiple
@@ -742,8 +742,8 @@ where:
2. popcount(![x]) = the number of bits that are 1 in ![x]
Initial tests of this surprising property seem to hold. As ![n] approaches
infinity, we end up with an average overhead of 2 pointers, which matches what
our assumption from earlier. During iteration, the popcount function seems to
infinity, we end up with an average overhead of 2 pointers, which matches our
assumption from earlier. During iteration, the popcount function seems to
handle deviations from this average. Of course, just to make sure I wrote a
quick script that verified this property for all 32-bit integers.
@@ -767,7 +767,7 @@ overflow, but we can avoid this by rearranging the equation a bit:
![off = N - (B-2w/8)n - (w/8)popcount(n)][ctz-formula7]
Our solution requires quite a bit of math, but computer are very good at math.
Our solution requires quite a bit of math, but computers are very good at math.
Now we can find both our block index and offset from a size in _O(1)_, letting
us store CTZ skip-lists with only a pointer and size.
@@ -850,7 +850,7 @@ nearly every write to the filesystem.
Normally, block allocation involves some sort of free list or bitmap stored on
the filesystem that is updated with free blocks. However, with power
resilience, keeping these structure consistent becomes difficult. It doesn't
resilience, keeping these structures consistent becomes difficult. It doesn't
help that any mistake in updating these structures can result in lost blocks
that are impossible to recover.
@@ -894,9 +894,9 @@ high-risk error conditions.
---
Our block allocator needs to find free blocks efficiently. You could traverse
through every block on storage and check each one against our filesystem tree,
however the runtime would be abhorrent. We need to somehow collect multiple
blocks each traversal.
through every block on storage and check each one against our filesystem tree;
however, the runtime would be abhorrent. We need to somehow collect multiple
blocks per traversal.
Looking at existing designs, some larger filesystems that use a similar "drop
it on the floor" strategy store a bitmap of the entire storage in [RAM]. This
@@ -920,8 +920,8 @@ a brute force traversal. Instead of a bitmap the size of storage, we keep track
of a small, fixed-size bitmap called the lookahead buffer. During block
allocation, we take blocks from the lookahead buffer. If the lookahead buffer
is empty, we scan the filesystem for more free blocks, populating our lookahead
buffer. Each scan we use an increasing offset, circling the storage as blocks
are allocated.
buffer. In each scan we use an increasing offset, circling the storage as
blocks are allocated.
Here's what it might look like to allocate 4 blocks on a decently busy
filesystem with a 32 bit lookahead and a total of 128 blocks (512 KiB
@@ -950,7 +950,7 @@ alloc = 112 lookahead: ffff8000
```
This lookahead approach has a runtime complexity of _O(n&sup2;)_ to completely
scan storage, however, bitmaps are surprisingly compact, and in practice only
scan storage; however, bitmaps are surprisingly compact, and in practice only
one or two passes are usually needed to find free blocks. Additionally, the
performance of the allocator can be optimized by adjusting the block size or
size of the lookahead buffer, trading either write granularity or RAM for
@@ -1173,9 +1173,9 @@ We may find that the new block is also bad, but hopefully after repeating this
cycle we'll eventually find a new block where a write succeeds. If we don't,
that means that all blocks in our storage are bad, and we've reached the end of
our device's usable life. At this point, littlefs will return an "out of space"
error, which is technically true, there are no more good blocks, but as an
added benefit also matches the error condition expected by users of dynamically
sized data.
error. This is technically true, as there are no more good blocks, but as an
added benefit it also matches the error condition expected by users of
dynamically sized data.
---
@@ -1187,7 +1187,7 @@ original data even after it has been corrupted. One such mechanism for this is
ECC is an extension to the idea of a checksum. Where a checksum such as CRC can
detect that an error has occurred in the data, ECC can detect and actually
correct some amount of errors. However, there is a limit to how many errors ECC
can detect, call the [Hamming bound][wikipedia-hamming-bound]. As the number of
can detect: the [Hamming bound][wikipedia-hamming-bound]. As the number of
errors approaches the Hamming bound, we may still be able to detect errors, but
can no longer fix the data. If we've reached this point the block is
unrecoverable.
@@ -1202,7 +1202,7 @@ chip itself.
In littlefs, ECC is entirely optional. Read errors can instead be prevented
proactively by wear leveling. But it's important to note that ECC can be used
at the block device level to modestly extend the life of a device. littlefs
respects any errors reported by the block device, allow a block device to
respects any errors reported by the block device, allowing a block device to
provide additional aggressive error detection.
---
@@ -1231,7 +1231,7 @@ Generally, wear leveling algorithms fall into one of two categories:
we need to consider all blocks, including blocks that already contain data.
As a tradeoff for code size and complexity, littlefs (currently) only provides
dynamic wear leveling. This is a best efforts solution. Wear is not distributed
dynamic wear leveling. This is a best effort solution. Wear is not distributed
perfectly, but it is distributed among the free blocks and greatly extends the
life of a device.
@@ -1378,7 +1378,7 @@ We can make several improvements. First, instead of giving each file its own
metadata pair, we can store multiple files in a single metadata pair. One way
to do this is to directly associate a directory with a metadata pair (or a
linked list of metadata pairs). This makes it easy for multiple files to share
the directory's metadata pair for logging and reduce the collective storage
the directory's metadata pair for logging and reduces the collective storage
overhead.
The strict binding of metadata pairs and directories also gives users
@@ -1816,12 +1816,12 @@ while manipulating the directory tree (foreshadowing!).
## The move problem
We have one last challenge. The move problem. Phrasing the problem is simple:
We have one last challenge: the move problem. Phrasing the problem is simple:
How do you atomically move a file between two directories?
In littlefs we can atomically commit to directories, but we can't create
an atomic commit that span multiple directories. The filesystem must go
an atomic commit that spans multiple directories. The filesystem must go
through a minimum of two distinct states to complete a move.
To make matters worse, file moves are a common form of synchronization for
@@ -1831,13 +1831,13 @@ atomic moves right.
So what can we do?
- We definitely can't just let power-loss result in duplicated or lost files.
This could easily break user's code and would only reveal itself in extreme
This could easily break users' code and would only reveal itself in extreme
cases. We were only able to be lazy about the threaded linked-list because
it isn't user facing and we can handle the corner cases internally.
- Some filesystems propagate COW operations up the tree until finding a common
parent. Unfortunately this interacts poorly with our threaded tree and brings
back the issue of upward propagation of wear.
- Some filesystems propagate COW operations up the tree until a common parent
is found. Unfortunately this interacts poorly with our threaded tree and
brings back the issue of upward propagation of wear.
- In a previous version of littlefs we tried to solve this problem by going
back and forth between the source and destination, marking and unmarking the
@@ -1852,7 +1852,7 @@ introduction of a mechanism called "global state".
---
Global state is a small set of state that can be updated from _any_ metadata
pair. Combining global state with metadata pair's ability to update multiple
pair. Combining global state with metadata pairs' ability to update multiple
entries in one commit gives us a powerful tool for crafting complex atomic
operations.
@@ -1910,7 +1910,7 @@ the filesystem is mounted.
You may have noticed that global state is very expensive. We keep a copy in
RAM and a delta in an unbounded number of metadata pairs. Even if we reset
the global state to its initial value we can't easily clean up the deltas on
the global state to its initial value, we can't easily clean up the deltas on
disk. For this reason, it's very important that we keep the size of global
state bounded and extremely small. But, even with a strict budget, global
state is incredibly valuable.

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

@@ -53,6 +53,7 @@ const struct lfs_config cfg = {
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
// entry point
@@ -109,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

View File

@@ -102,6 +102,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
fclose(f);
return err;
}
@@ -124,6 +125,7 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_create -> %"PRId32, err);
fclose(f);
return err;
}
@@ -178,6 +180,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_read -> %d", err);
fclose(f);
return err;
}
@@ -185,6 +188,7 @@ int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
if (res < size && !feof(f)) {
err = -errno;
LFS_TRACE("lfs_emubd_read -> %d", err);
fclose(f);
return err;
}
@@ -230,6 +234,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}
@@ -237,6 +242,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (res < size) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}
@@ -244,6 +250,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (err) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}
@@ -252,6 +259,7 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_prog -> %d", err);
fclose(f);
return err;
}
@@ -340,6 +348,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
int err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}
@@ -364,6 +373,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}
@@ -388,6 +398,7 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
if (res < 1) {
err = -errno;
LFS_TRACE("lfs_emubd_sync -> %d", err);
fclose(f);
return err;
}

156
lfs.c
View File

@@ -352,7 +352,7 @@ static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a,
static inline void lfs_gstate_xororphans(struct lfs_gstate *a,
const struct lfs_gstate *b, bool orphans) {
a->tag ^= LFS_MKTAG(0x800, 0, 0) & (b->tag ^ (orphans << 31));
a->tag ^= LFS_MKTAG(0x800, 0, 0) & (b->tag ^ ((uint32_t)orphans << 31));
}
static inline void lfs_gstate_xormove(struct lfs_gstate *a,
@@ -846,7 +846,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
}
// reset the next bit if we need to
ptag ^= (lfs_tag_chunk(tag) & 1U) << 31;
ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31;
// toss our crc into the filesystem seed for
// pseudorandom numbers
@@ -929,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
@@ -975,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 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,
@@ -1010,7 +1016,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name);
if (tag < 0) {
return tag;
return (int)tag;
}
info->type = lfs_tag_type3(tag);
@@ -1019,7 +1025,7 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0),
LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz);
if (tag < 0) {
return tag;
return (int)tag;
}
lfs_ctz_fromle32(&ctz);
@@ -1062,7 +1068,7 @@ static int lfs_dir_find_match(void *data,
return LFS_CMP_EQ;
}
static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
const char **path, uint16_t *id) {
// we reduce path to a single name if we can find it
const char *name = *path;
@@ -1275,7 +1281,7 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
}
commit->off += sizeof(tag)+lfs_tag_size(tag);
commit->ptag = tag ^ (reset << 31);
commit->ptag = tag ^ ((lfs_tag_t)reset << 31);
commit->crc = LFS_BLOCK_NULL; // reset crc for next "commit"
}
@@ -1377,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) {
@@ -1503,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
@@ -1643,7 +1654,6 @@ relocate:
if (err && (err != LFS_ERR_NOSPC && !exhausted)) {
return err;
}
continue;
}
@@ -1936,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;
}
@@ -1955,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);
@@ -2064,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) {
@@ -2080,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;
}
}
@@ -2103,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);
@@ -2738,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;
}
@@ -2761,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 {
@@ -2781,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 {
@@ -2789,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;
}
}
@@ -2818,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;
}
}
@@ -2829,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;
}
@@ -2855,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;
}
}
@@ -2872,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;
}
@@ -2887,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 {
@@ -2908,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;
}
@@ -2917,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;
}
}
@@ -2944,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;
}
@@ -2960,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;
}
@@ -3006,10 +3020,10 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
} else if (size > oldsize) {
// flush+seek if not already at end
if (file->pos != oldsize) {
int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
if (err < 0) {
LFS_TRACE("lfs_file_truncate -> %d", err);
return err;
lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
if (res < 0) {
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
}
@@ -3017,17 +3031,17 @@ 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);
return res;
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
}
}
// restore pos
int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
if (err < 0) {
LFS_TRACE("lfs_file_truncate -> %d", err);
return err;
lfs_soff_t res = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
if (res < 0) {
LFS_TRACE("lfs_file_truncate -> %"PRId32, res);
return (int)res;
}
LFS_TRACE("lfs_file_truncate -> %d", 0);
@@ -3046,8 +3060,8 @@ 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);
return res;
LFS_TRACE("lfs_file_rewind -> %"PRId32, res);
return (int)res;
}
LFS_TRACE("lfs_file_rewind -> %d", 0);
@@ -3075,8 +3089,8 @@ 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);
return tag;
LFS_TRACE("lfs_stat -> %"PRId32, tag);
return (int)tag;
}
int err = lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info);
@@ -3096,8 +3110,8 @@ 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);
return (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;
}
lfs_mdir_t dir;
@@ -3107,8 +3121,8 @@ 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);
return res;
LFS_TRACE("lfs_remove -> %"PRId32, res);
return (int)res;
}
lfs_pair_fromle32(pair);
@@ -3170,8 +3184,8 @@ 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);
return (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;
}
// find new entry
@@ -3180,8 +3194,8 @@ 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);
return (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;
}
lfs_mdir_t prevdir;
@@ -3201,8 +3215,8 @@ 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);
return res;
LFS_TRACE("lfs_rename -> %"PRId32, res);
return (int)res;
}
lfs_pair_fromle32(prevpair);
@@ -3296,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;
}
}
@@ -3307,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;
}
@@ -3369,6 +3383,12 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
lfs->cfg = cfg;
int err = 0;
// validate that the lfs-cfg sizes were initiated properly before
// performing any arithmetic logics with them
LFS_ASSERT(lfs->cfg->read_size != 0);
LFS_ASSERT(lfs->cfg->prog_size != 0);
LFS_ASSERT(lfs->cfg->cache_size != 0);
// check that block size is a multiple of cache size is a multiple
// of prog and read sizes
LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0);
@@ -3757,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);
@@ -3881,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
@@ -4066,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;
}
@@ -4608,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;
@@ -4631,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

View File

@@ -179,7 +179,7 @@ echo "--- Really big path test ---"
scripts/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
memset(path, 'w', LFS_NAME_MAX);
path[LFS_NAME_MAX+1] = '\0';
path[LFS_NAME_MAX] = '\0';
lfs_mkdir(&lfs, path) => 0;
lfs_remove(&lfs, path) => 0;
lfs_file_open(&lfs, &file, path,
@@ -189,7 +189,7 @@ scripts/test.py << TEST
memcpy(path, "coffee/", strlen("coffee/"));
memset(path+strlen("coffee/"), 'w', LFS_NAME_MAX);
path[strlen("coffee/")+LFS_NAME_MAX+1] = '\0';
path[strlen("coffee/")+LFS_NAME_MAX] = '\0';
lfs_mkdir(&lfs, path) => 0;
lfs_remove(&lfs, path) => 0;
lfs_file_open(&lfs, &file, path,

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