Compare commits

..

2 Commits

Author SHA1 Message Date
Christopher Haster
ebc0d24211 Added conversion to/from little-endian on disk
Required to support big-endian processors, with the most notable being
the PowerPC architecture.

On little-endian architectures, these conversions can be optimized out
and have no code impact.

Initial patch provided by gmouchard
2018-02-15 16:33:03 -06:00
Christopher Haster
dbce53672b Added conversion to/from little endian on disk
patch provided by gmouchard
2018-02-04 11:51:23 -06:00
39 changed files with 4879 additions and 7836 deletions

9
.gitignore vendored
View File

@@ -1,9 +0,0 @@
# Compilation output
*.o
*.d
*.a
# Testing things
blocks/
lfs2
test.c

View File

@@ -1,197 +1,47 @@
# Environment variables
env:
global:
- CFLAGS=-Werror
# Common test script
script:
# make sure example can at least compile
- sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c &&
make all CFLAGS+="
# make sure example can at least compile
- sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c &&
CFLAGS='
-Duser_provided_block_device_read=NULL
-Duser_provided_block_device_prog=NULL
-Duser_provided_block_device_erase=NULL
-Duser_provided_block_device_sync=NULL
-include stdio.h"
-include stdio.h -Werror' make all size
# run tests
- make test QUIET=1
# run tests
- make test QUIET=1
# run tests with a few different configurations
- make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=1 -DLFS2_CACHE_SIZE=4"
- make test QUIET=1 CFLAGS+="-DLFS2_READ_SIZE=512 -DLFS2_CACHE_SIZE=512 -DLFS2_BLOCK_CYCLES=16"
- make test QUIET=1 CFLAGS+="-DLFS2_BLOCK_COUNT=1023 -DLFS2_LOOKAHEAD_SIZE=256"
# run tests with a few different configurations
- CFLAGS="-DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1" make test QUIET=1
- CFLAGS="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512" make test QUIET=1
- CFLAGS="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048" make test QUIET=1
- make clean test QUIET=1 CFLAGS+="-DLFS2_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS2_NO_INTRINSICS"
# self-host with littlefs-fuse for fuzz test
- make -C littlefs-fuse
# compile and find the code size with the smallest configuration
- make clean size
OBJ="$(ls lfs2*.o | tr '\n' ' ')"
CFLAGS+="-DLFS2_NO_ASSERT -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR"
| tee sizes
- littlefs-fuse/lfs --format /dev/loop0
- littlefs-fuse/lfs /dev/loop0 mount
# update status if we succeeded, compare with master if possible
- |
if [ "$TRAVIS_TEST_RESULT" -eq 0 ]
then
CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl -u $GEKY_BOT_STATUSES https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"$STAGE/$NAME\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \
|| echo 0)
- ls mount
- mkdir mount/littlefs
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
- cd mount/littlefs
- ls
- make -B test_dirs test_files QUIET=1
STATUS="Passed, code size is ${CURR}B"
if [ "$PREV" -ne 0 ]
then
STATUS="$STATUS ($(python -c "print '%+.2f' % (100*($CURR-$PREV)/$PREV.0)")%)"
fi
fi
# CI matrix
jobs:
include:
# native testing
- stage: test
env:
- STAGE=test
- NAME=littlefs-x86
# cross-compile with ARM (thumb mode)
- stage: test
env:
- STAGE=test
- NAME=littlefs-arm
- CC="arm-linux-gnueabi-gcc --static -mthumb"
- EXEC="qemu-arm"
install:
- sudo apt-get install gcc-arm-linux-gnueabi qemu-user
- arm-linux-gnueabi-gcc --version
- qemu-arm -version
# cross-compile with PowerPC
- stage: test
env:
- STAGE=test
- NAME=littlefs-powerpc
- CC="powerpc-linux-gnu-gcc --static"
- EXEC="qemu-ppc"
install:
- sudo apt-get install gcc-powerpc-linux-gnu qemu-user
- powerpc-linux-gnu-gcc --version
- qemu-ppc -version
# cross-compile with MIPS
- stage: test
env:
- STAGE=test
- NAME=littlefs-mips
- CC="mips-linux-gnu-gcc --static"
- EXEC="qemu-mips"
install:
- sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main universe"
- sudo apt-get -qq update
- sudo apt-get install gcc-mips-linux-gnu qemu-user
- mips-linux-gnu-gcc --version
- qemu-mips -version
# Automatically update releases
- stage: deploy
env:
- STAGE=deploy
- NAME=deploy
script:
# Find version defined in lfs2.h
- LFS2_VERSION=$(grep -ox '#define LFS2_VERSION .*' lfs2.h | cut -d ' ' -f3)
- LFS2_VERSION_MAJOR=$((0xffff & ($LFS2_VERSION >> 16)))
- LFS2_VERSION_MINOR=$((0xffff & ($LFS2_VERSION >> 0)))
# Grab latests patch from repo tags, default to 0, needs finagling to get past github's pagination api
- PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR.
- PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I
| sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1'
|| echo $PREV_URL)
- LFS2_VERSION_PATCH=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL"
| jq 'map(.ref | match("\\bv.*\\..*\\.(.*)$";"g")
.captures[].string | tonumber) | max + 1'
|| echo 0)
# We have our new version
- LFS2_VERSION="v$LFS2_VERSION_MAJOR.$LFS2_VERSION_MINOR.$LFS2_VERSION_PATCH"
- echo "VERSION $LFS2_VERSION"
- |
# Check that we're the most recent commit
CURRENT_COMMIT=$(curl -f -u "$GEKY_BOT_RELEASES" \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/commits/master \
| jq -re '.sha')
if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
then
# Create a simple tag
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{
\"ref\": \"refs/tags/$LFS2_VERSION\",
\"sha\": \"$TRAVIS_COMMIT\"
}"
# Minor release?
if [[ "$LFS2_VERSION" == *.0 ]]
then
# Build release notes
PREV=$(git tag --sort=-v:refname -l "v*.0" | head -1)
if [ ! -z "$PREV" ]
then
echo "PREV $PREV"
CHANGES=$'### Changes\n\n'$( \
git log --oneline $PREV.. --grep='^Merge' --invert-grep)
printf "CHANGES\n%s\n\n" "$CHANGES"
fi
# Create the release
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
-d "{
\"tag_name\": \"$LFS2_VERSION\",
\"name\": \"${LFS2_VERSION%.0}\",
\"draft\": true,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}"
fi
fi
# Manage statuses
before_install:
- |
curl -u $GEKY_BOT_STATUSES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
-d "{
\"context\": \"$STAGE/$NAME\",
\"state\": \"pending\",
\"description\": \"${STATUS:-In progress}\",
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
}"
- fusermount -V
- gcc --version
after_failure:
- |
curl -u $GEKY_BOT_STATUSES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
-d "{
\"context\": \"$STAGE/$NAME\",
\"state\": \"failure\",
\"description\": \"${STATUS:-Failed}\",
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
}"
install:
- sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse
after_success:
- |
curl -u $GEKY_BOT_STATUSES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT} \
-d "{
\"context\": \"$STAGE/$NAME\",
\"state\": \"success\",
\"description\": \"${STATUS:-Passed}\",
\"target_url\": \"https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID\"
}"
before_script:
- rm -rf littlefs-fuse/littlefs/*
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
# Job control
stages:
- name: test
- name: deploy
if: branch = master AND type = push
- mkdir mount
- sudo chmod a+rw /dev/loop0
- dd if=/dev/zero bs=512 count=2048 of=disk
- losetup /dev/loop0 disk

View File

@@ -27,17 +27,16 @@ cheap, and can be very granular. For NOR flash specifically, byte-level
programs are quite common. Erasing, however, requires an expensive operation
that forces the state of large blocks of memory to reset in a destructive
reaction that gives flash its name. The [Wikipedia entry](https://en.wikipedia.org/wiki/Flash_memory)
has more information if you are interested in how this works.
has more information if you are interesting in how this works.
This leaves us with an interesting set of limitations that can be simplified
to three strong requirements:
1. **Power-loss resilient** - This is the main goal of the littlefs and the
focus of this project.
Embedded systems are usually designed without a shutdown routine and a
notable lack of user interface for recovery, so filesystems targeting
embedded systems must be prepared to lose power at any given time.
focus of this project. Embedded systems are usually designed without a
shutdown routine and a notable lack of user interface for recovery, so
filesystems targeting embedded systems must be prepared to lose power an
any given time.
Despite this state of things, there are very few embedded filesystems that
handle power loss in a reasonable manner, and most can become corrupted if
@@ -53,8 +52,7 @@ to three strong requirements:
which stores a file allocation table (FAT) at a specific offset from the
beginning of disk. Every block allocation will update this table, and after
100,000 updates, the block will likely go bad, rendering the filesystem
unusable even if there are many more erase cycles available on the storage
as a whole.
unusable even if there are many more erase cycles available on the storage.
3. **Bounded RAM/ROM** - Even with the design difficulties presented by the
previous two limitations, we have already seen several flash filesystems
@@ -74,7 +72,7 @@ to three strong requirements:
## Existing designs?
There are of course, many different existing filesystem. Here is a very rough
There are of course, many different existing filesystem. Heres a very rough
summary of the general ideas behind some of them.
Most of the existing filesystems fall into the one big category of filesystem
@@ -82,21 +80,21 @@ designed in the early days of spinny magnet disks. While there is a vast amount
of interesting technology and ideas in this area, the nature of spinny magnet
disks encourage properties, such as grouping writes near each other, that don't
make as much sense on recent storage types. For instance, on flash, write
locality is not important and can actually increase wear.
locality is not important and can actually increase wear destructively.
One of the most popular designs for flash filesystems is called the
[logging filesystem](https://en.wikipedia.org/wiki/Log-structured_file_system).
The flash filesystems [jffs](https://en.wikipedia.org/wiki/JFFS)
and [yaffs](https://en.wikipedia.org/wiki/YAFFS) are good examples. In a
logging filesystem, data is not stored in a data structure on disk, but instead
and [yaffs](https://en.wikipedia.org/wiki/YAFFS) are good examples. In
logging filesystem, data is not store in a data structure on disk, but instead
the changes to the files are stored on disk. This has several neat advantages,
such as the fact that the data is written in a cyclic log format and naturally
such as the fact that the data is written in a cyclic log format naturally
wear levels as a side effect. And, with a bit of error detection, the entire
filesystem can easily be designed to be resilient to power loss. The
journaling component of most modern day filesystems is actually a reduced
journalling component of most modern day filesystems is actually a reduced
form of a logging filesystem. However, logging filesystems have a difficulty
scaling as the size of storage increases. And most filesystems compensate by
caching large parts of the filesystem in RAM, a strategy that is inappropriate
caching large parts of the filesystem in RAM, a strategy that is unavailable
for embedded systems.
Another interesting filesystem design technique is that of [copy-on-write (COW)](https://en.wikipedia.org/wiki/Copy-on-write).
@@ -109,14 +107,14 @@ where the COW data structures are synchronized.
## Metadata pairs
The core piece of technology that provides the backbone for the littlefs is
the concept of metadata pairs. The key idea here is that any metadata that
the concept of metadata pairs. The key idea here, is that any metadata that
needs to be updated atomically is stored on a pair of blocks tagged with
a revision count and checksum. Every update alternates between these two
pairs, so that at any time there is always a backup containing the previous
state of the metadata.
Consider a small example where each metadata pair has a revision count,
a number as data, and the XOR of the block as a quick checksum. If
a number as data, and the xor of the block as a quick checksum. If
we update the data to a value of 9, and then to a value of 5, here is
what the pair of blocks may look like after each update:
```
@@ -132,7 +130,7 @@ what the pair of blocks may look like after each update:
After each update, we can find the most up to date value of data by looking
at the revision count.
Now consider what the blocks may look like if we suddenly lose power while
Now consider what the blocks may look like if we suddenly loss power while
changing the value of data to 5:
```
block 1 block 2 block 1 block 2 block 1 block 2
@@ -151,7 +149,7 @@ check our checksum we notice that block 1 was corrupted. So we fall back to
block 2 and use the value 9.
Using this concept, the littlefs is able to update metadata blocks atomically.
There are a few other tweaks, such as using a 32 bit CRC and using sequence
There are a few other tweaks, such as using a 32 bit crc and using sequence
arithmetic to handle revision count overflow, but the basic concept
is the same. These metadata pairs define the backbone of the littlefs, and the
rest of the filesystem is built on top of these atomic updates.
@@ -163,7 +161,7 @@ requires two blocks for each block of data. I'm sure users would be very
unhappy if their storage was suddenly cut in half! Instead of storing
everything in these metadata blocks, the littlefs uses a COW data structure
for files which is in turn pointed to by a metadata block. When
we update a file, we create copies of any blocks that are modified until
we update a file, we create a copies of any blocks that are modified until
the metadata blocks are updated with the new copy. Once the metadata block
points to the new copy, we deallocate the old blocks that are no longer in use.
@@ -186,7 +184,7 @@ Here is what updating a one-block file may look like:
update data in file update metadata pair
```
It doesn't matter if we lose power while writing new data to block 5,
It doesn't matter if we lose power while writing block 5 with the new data,
since the old data remains unmodified in block 4. This example also
highlights how the atomic updates of the metadata blocks provide a
synchronization barrier for the rest of the littlefs.
@@ -208,7 +206,7 @@ files in filesystems. Of these, the littlefs uses a rather unique [COW](https://
data structure that allows the filesystem to reuse unmodified parts of the
file without additional metadata pairs.
First lets consider storing files in a simple linked-list. What happens when we
First lets consider storing files in a simple linked-list. What happens when
append a block? We have to change the last block in the linked-list to point
to this new block, which means we have to copy out the last block, and change
the second-to-last block, and then the third-to-last, and so on until we've
@@ -242,8 +240,8 @@ Exhibit B: A backwards linked-list
```
However, a backwards linked-list does come with a rather glaring problem.
Iterating over a file _in order_ has a runtime cost of O(n^2). Gah! A quadratic
runtime to just _read_ a file? That's awful. Keep in mind reading files is
Iterating over a file _in order_ has a runtime of O(n^2). Gah! A quadratic
runtime to just _read_ a file? That's awful. Keep in mind reading files are
usually the most common filesystem operation.
To avoid this problem, the littlefs uses a multilayered linked-list. For
@@ -268,7 +266,7 @@ Exhibit C: A backwards CTZ skip-list
```
The additional pointers allow us to navigate the data-structure on disk
much more efficiently than in a singly linked-list.
much more efficiently than in a single linked-list.
Taking exhibit C for example, here is the path from data block 5 to data
block 1. You can see how data block 3 was completely skipped:
@@ -291,15 +289,15 @@ The path to data block 0 is even more quick, requiring only two jumps:
We can find the runtime complexity by looking at the path to any block from
the block containing the most pointers. Every step along the path divides
the search space for the block in half. This gives us a runtime of O(log n).
the search space for the block in half. This gives us a runtime of O(logn).
To get to the block with the most pointers, we can perform the same steps
backwards, which puts the runtime at O(2 log n) = O(log n). The interesting
backwards, which puts the runtime at O(2logn) = O(logn). The interesting
part about this data structure is that this optimal path occurs naturally
if we greedily choose the pointer that covers the most distance without passing
our target block.
So now we have a representation of files that can be appended trivially with
a runtime of O(1), and can be read with a worst case runtime of O(n log n).
a runtime of O(1), and can be read with a worst case runtime of O(nlogn).
Given that the the runtime is also divided by the amount of data we can store
in a block, this is pretty reasonable.
@@ -364,7 +362,7 @@ N = file size in bytes
And this works quite well, but is not trivial to calculate. This equation
requires O(n) to compute, which brings the entire runtime of reading a file
to O(n^2 log n). Fortunately, the additional O(n) does not need to touch disk,
to O(n^2logn). Fortunately, the additional O(n) does not need to touch disk,
so it is not completely unreasonable. But if we could solve this equation into
a form that is easily computable, we can avoid a big slowdown.
@@ -381,11 +379,11 @@ unintuitive property:
![mindblown](https://latex.codecogs.com/svg.latex?%5Csum_i%5En%5Cleft%28%5Ctext%7Bctz%7D%28i%29&plus;1%5Cright%29%20%3D%202n-%5Ctext%7Bpopcount%7D%28n%29)
where:
ctz(x) = the number of trailing bits that are 0 in x
popcount(x) = the number of bits that are 1 in x
ctz(i) = the number of trailing bits that are 0 in i
popcount(i) = the number of bits that are 1 in i
It's a bit bewildering that these two seemingly unrelated bitwise instructions
are related by this property. But if we start to dissect this equation we can
are related by this property. But if we start to disect this equation we can
see that it does hold. As n approaches infinity, we do end up with an average
overhead of 2 pointers as we find earlier. And popcount seems to handle the
error from this average as it accumulates in the CTZ skip-list.
@@ -412,7 +410,8 @@ a bit to avoid integer overflow:
![formulaforoff](https://latex.codecogs.com/svg.latex?%5Cmathit%7Boff%7D%20%3D%20N%20-%20%5Cleft%28B-2%5Cfrac%7Bw%7D%7B8%7D%5Cright%29n%20-%20%5Cfrac%7Bw%7D%7B8%7D%5Ctext%7Bpopcount%7D%28n%29)
The solution involves quite a bit of math, but computers are very good at math.
Now we can solve for both the block index and offset from the file size in O(1).
We can now solve for the block index + offset while only needed to store the
file size in O(1).
Here is what it might look like to update a file stored with a CTZ skip-list:
```
@@ -501,17 +500,16 @@ scanned to find the most recent free list, but once the list was found the
state of all free blocks becomes known.
However, this approach had several issues:
- There was a lot of nuanced logic for adding blocks to the free list without
modifying the blocks, since the blocks remain active until the metadata is
updated.
- The free list had to support both additions and removals in FIFO order while
- The free list had to support both additions and removals in fifo order while
minimizing block erases.
- The free list had to handle the case where the file system completely ran
out of blocks and may no longer be able to add blocks to the free list.
- If we used a revision count to track the most recently updated free list,
metadata blocks that were left unmodified were ticking time bombs that would
cause the system to go haywire if the revision count overflowed.
cause the system to go haywire if the revision count overflowed
- Every single metadata block wasted space to store these free list references.
Actually, to simplify, this approach had one massive glaring issue: complexity.
@@ -541,7 +539,7 @@ would have an abhorrent runtime.
So the littlefs compromises. It doesn't store a bitmap the size of the storage,
but it does store a little bit-vector that contains a fixed set lookahead
for block allocations. During a block allocation, the lookahead vector is
checked for any free blocks. If there are none, the lookahead region jumps
checked for any free blocks, if there are none, the lookahead region jumps
forward and the entire filesystem is scanned for free blocks.
Here's what it might look like to allocate 4 blocks on a decently busy
@@ -624,7 +622,7 @@ So, as a solution, the littlefs adopted a sort of threaded tree. Each
directory not only contains pointers to all of its children, but also a
pointer to the next directory. These pointers create a linked-list that
is threaded through all of the directories in the filesystem. Since we
only use this linked list to check for existence, the order doesn't actually
only use this linked list to check for existance, the order doesn't actually
matter. As an added plus, we can repurpose the pointer for the individual
directory linked-lists and avoid using any additional space.
@@ -775,7 +773,7 @@ deorphan step that simply iterates through every directory in the linked-list
and checks it against every directory entry in the filesystem to see if it
has a parent. The deorphan step occurs on the first block allocation after
boot, so orphans should never cause the littlefs to run out of storage
prematurely. Note that the deorphan step never needs to run in a read-only
prematurely. Note that the deorphan step never needs to run in a readonly
filesystem.
## The move problem
@@ -885,7 +883,7 @@ a power loss will occur during filesystem activity. We still need to handle
the condition, but runtime during a power loss takes a back seat to the runtime
during normal operations.
So what littlefs does is inelegantly simple. When littlefs moves a file, it
So what littlefs does is unelegantly simple. When littlefs moves a file, it
marks the file as "moving". This is stored as a single bit in the directory
entry and doesn't take up much space. Then littlefs moves the directory,
finishing with the complete remove of the "moving" directory entry.
@@ -981,7 +979,7 @@ if it exists elsewhere in the filesystem.
So now that we have all of the pieces of a filesystem, we can look at a more
subtle attribute of embedded storage: The wear down of flash blocks.
The first concern for the littlefs, is that perfectly valid blocks can suddenly
The first concern for the littlefs, is that prefectly valid blocks can suddenly
become unusable. As a nice side-effect of using a COW data-structure for files,
we can simply move on to a different block when a file write fails. All
modifications to files are performed in copies, so we will only replace the
@@ -1153,7 +1151,7 @@ develops errors and needs to be moved.
## Wear leveling
The second concern for the littlefs is that blocks in the filesystem may wear
The second concern for the littlefs, is that blocks in the filesystem may wear
unevenly. In this situation, a filesystem may meet an early demise where
there are no more non-corrupted blocks that aren't in use. It's common to
have files that were written once and left unmodified, wasting the potential
@@ -1173,7 +1171,7 @@ of wear leveling:
In littlefs's case, it's possible to use the revision count on metadata pairs
to approximate the wear of a metadata block. And combined with the COW nature
of files, littlefs could provide your usual implementation of dynamic wear
of files, littlefs could provide your usually implementation of dynamic wear
leveling.
However, the littlefs does not. This is for a few reasons. Most notably, even
@@ -1212,9 +1210,9 @@ So, to summarize:
metadata block is active
4. Directory blocks contain either references to other directories or files
5. Files are represented by copy-on-write CTZ skip-lists which support O(1)
append and O(n log n) reading
append and O(nlogn) reading
6. Blocks are allocated by scanning the filesystem for used blocks in a
fixed-size lookahead region that is stored in a bit-vector
fixed-size lookahead region is that stored in a bit-vector
7. To facilitate scanning the filesystem, all directories are part of a
linked-list that is threaded through the entire filesystem
8. If a block develops an error, the littlefs allocates a new block, and

View File

@@ -1,24 +1,165 @@
Copyright (c) 2017, Arm Limited. All rights reserved.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
1. Definitions.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.

View File

@@ -1,11 +1,8 @@
TARGET = lfs2.a
ifneq ($(wildcard test.c main.c),)
override TARGET = lfs2
endif
TARGET = lfs
CC ?= gcc
AR ?= ar
SIZE ?= size
CC = gcc
AR = ar
SIZE = size
SRC += $(wildcard *.c emubd/*.c)
OBJ := $(SRC:.c=.o)
@@ -17,16 +14,15 @@ TEST := $(patsubst tests/%.sh,%,$(wildcard tests/test_*))
SHELL = /bin/bash -o pipefail
ifdef DEBUG
override CFLAGS += -O0 -g3
CFLAGS += -O0 -g3
else
override CFLAGS += -Os
CFLAGS += -Os
endif
ifdef WORD
override CFLAGS += -m$(WORD)
CFLAGS += -m$(WORD)
endif
override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wshadow -Wunused-parameter -Wjump-misses-init -Wsign-compare
CFLAGS += -I.
CFLAGS += -std=c99 -Wall -pedantic
all: $(TARGET)
@@ -37,21 +33,18 @@ size: $(OBJ)
$(SIZE) -t $^
.SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate \
test_entries test_interspersed test_alloc test_paths test_attrs \
test_move test_orphan test_corrupt
@rm test.c
test: test_format test_dirs test_files test_seek test_truncate test_parallel \
test_alloc test_paths test_orphan test_move test_corrupt
test_%: tests/test_%.sh
ifdef QUIET
@./$< | sed -n '/^[-=]/p'
./$< | sed -n '/^[-=]/p'
else
./$<
endif
-include $(DEP)
lfs2: $(OBJ)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
%.a: $(OBJ)

View File

@@ -16,7 +16,7 @@ of memory. Recursion is avoided and dynamic memory is limited to configurable
buffers that can be provided statically.
**Power-loss resilient** - The littlefs is designed for systems that may have
random power failures. The littlefs has strong copy-on-write guarantees and
random power failures. The littlefs has strong copy-on-write guaruntees and
storage on disk is always kept in a valid state.
**Wear leveling** - Since the most common form of embedded storage is erodible
@@ -30,14 +30,14 @@ main runs. The program can be interrupted at any time without losing track
of how many times it has been booted and without corrupting the filesystem:
``` c
#include "lfs2.h"
#include "lfs.h"
// variables used by the filesystem
lfs2_t lfs2;
lfs2_file_t file;
lfs_t lfs;
lfs_file_t file;
// configuration of the filesystem is provided by this struct
const struct lfs2_config cfg = {
const struct lfs_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
@@ -49,37 +49,36 @@ const struct lfs2_config cfg = {
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.lookahead = 128,
};
// entry point
int main(void) {
// mount the filesystem
int err = lfs2_mount(&lfs2, &cfg);
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err) {
lfs2_format(&lfs2, &cfg);
lfs2_mount(&lfs2, &cfg);
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
// read current count
uint32_t boot_count = 0;
lfs2_file_open(&lfs2, &file, "boot_count", LFS2_O_RDWR | LFS2_O_CREAT);
lfs2_file_read(&lfs2, &file, &boot_count, sizeof(boot_count));
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs2_file_rewind(&lfs2, &file);
lfs2_file_write(&lfs2, &file, &boot_count, sizeof(boot_count));
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs2_file_close(&lfs2, &file);
lfs_file_close(&lfs, &file);
// release any resources we were using
lfs2_unmount(&lfs2);
lfs_unmount(&lfs);
// print the boot count
printf("boot_count: %d\n", boot_count);
@@ -89,7 +88,7 @@ int main(void) {
## Usage
Detailed documentation (or at least as much detail as is currently available)
can be found in the comments in [lfs2.h](lfs2.h).
can be cound in the comments in [lfs.h](lfs.h).
As you may have noticed, littlefs takes in a configuration structure that
defines how the filesystem operates. The configuration struct provides the
@@ -97,36 +96,29 @@ filesystem with the block device operations and dimensions, tweakable
parameters that tradeoff memory usage for performance, and optional
static buffers if the user wants to avoid dynamic memory.
The state of the littlefs is stored in the `lfs2_t` type which is left up
The state of the littlefs is stored in the `lfs_t` type which is left up
to the user to allocate, allowing multiple filesystems to be in use
simultaneously. With the `lfs2_t` and configuration struct, a user can
simultaneously. With the `lfs_t` and configuration struct, a user can
format a block device or mount the filesystem.
Once mounted, the littlefs provides a full set of POSIX-like file and
Once mounted, the littlefs provides a full set of posix-like file and
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 actually committed to the
All posix operations, such as remove and rename, are atomic, even in event
of power-loss. Additionally, no file updates are actually commited to the
filesystem until sync or close is called on the file.
## Other notes
All littlefs have the potential to return a negative error code. The errors
can be either one of those found in the `enum lfs2_error` in [lfs2.h](lfs2.h),
can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h),
or an error returned by the user's block device operations.
In the configuration struct, the `prog` and `erase` function provided by the
user may return a `LFS2_ERR_CORRUPT` error if the implementation already can
detect corrupt blocks. However, the wear leveling does not depend on the return
code of these functions, instead all data is read back and checked for
integrity.
If your storage caches writes, make sure that the provided `sync` function
flushes all the data to memory and ensures that the next read fetches the data
from memory, otherwise data integrity can not be guaranteed. If the `write`
function does not perform caching, and therefore each `read` or `write` call
hits the memory, the `sync` function can simply return 0.
It should also be noted that the current implementation of littlefs doesn't
really do anything to insure that the data written to disk is machine portable.
This is fine as long as all of the involved machines share endianness
(little-endian) and don't have strange padding requirements.
## Reference material
@@ -139,27 +131,14 @@ with all the nitty-gritty details. Can be useful for developing tooling.
## Testing
The littlefs comes with a test suite designed to run on a PC using the
[emulated block device](emubd/lfs2_emubd.h) found in the emubd directory.
The tests assume a Linux environment and can be started with make:
The littlefs comes with a test suite designed to run on a pc using the
[emulated block device](emubd/lfs_emubd.h) found in the emubd directory.
The tests assume a linux environment and can be started with make:
``` bash
make test
```
## License
The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html)
license. See [LICENSE.md](LICENSE.md) for more information. Contributions to
this project are accepted under the same license.
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/
## Related projects
[Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) -
@@ -169,25 +148,10 @@ littlefs is available in Mbed OS as the [LittleFileSystem](https://os.mbed.com/d
class.
[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse)
wrapper for littlefs. The project allows you to mount littlefs directly on a
wrapper for littlefs. The project allows you to mount littlefs directly in a
Linux machine. Can be useful for debugging littlefs if you have an SD card
handy.
[littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for
littlefs. I'm not sure why you would want this, but it is handy for demos.
You can see it in action [here](http://littlefs.geky.net/demo.html).
[mklfs](https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src) -
A command line tool built by the [Lua RTOS](https://github.com/whitecatboard/Lua-RTOS-ESP32)
guys for making littlefs images from a host PC. Supports Windows, Mac OS,
and Linux.
[SPIFFS](https://github.com/pellepl/spiffs) - Another excellent embedded
filesystem for NOR flash. As a more traditional logging filesystem with full
static wear-leveling, SPIFFS will likely outperform littlefs on small
memories such as the internal flash on microcontrollers.
[Dhara](https://github.com/dlbeer/dhara) - An interesting NAND flash
translation layer designed for small MCUs. It offers static wear-leveling and
power-resilience with only a fixed O(|address|) pointer structure stored on
each block and in RAM.

12
SPEC.md
View File

@@ -46,7 +46,7 @@ Here's the layout of metadata blocks on disk:
| 0x04 | 32 bits | dir size |
| 0x08 | 64 bits | tail pointer |
| 0x10 | size-16 bytes | dir entries |
| 0x00+s | 32 bits | CRC |
| 0x00+s | 32 bits | crc |
**Revision count** - Incremented every update, only the uncorrupted
metadata-block with the most recent revision count contains the valid metadata.
@@ -75,7 +75,7 @@ Here's an example of a simple directory stored on disk:
(32 bits) revision count = 10 (0x0000000a)
(32 bits) dir size = 154 bytes, end of dir (0x0000009a)
(64 bits) tail pointer = 37, 36 (0x00000025, 0x00000024)
(32 bits) CRC = 0xc86e3106
(32 bits) crc = 0xc86e3106
00000000: 0a 00 00 00 9a 00 00 00 25 00 00 00 24 00 00 00 ........%...$...
00000010: 22 08 00 03 05 00 00 00 04 00 00 00 74 65 61 22 "...........tea"
@@ -138,12 +138,12 @@ not include the entry type size, attributes, or name. The full size in bytes
of the entry is 4 + entry length + attribute length + name length.
**Attribute length** - Length of system-specific attributes in bytes. Since
attributes are system specific, there is not much guarantee on the values in
attributes are system specific, there is not much garuntee on the values in
this section, and systems are expected to work even when it is empty. See the
[attributes](#entry-attributes) section for more details.
**Name length** - Length of the entry name. Entry names are stored as UTF8,
although most systems will probably only support ASCII. Entry names can not
**Name length** - Length of the entry name. Entry names are stored as utf8,
although most systems will probably only support ascii. Entry names can not
contain '/' and can not be '.' or '..' as these are a part of the syntax of
filesystem paths.
@@ -222,7 +222,7 @@ Here's an example of a complete superblock:
(32 bits) block count = 1024 blocks (0x00000400)
(32 bits) version = 1.1 (0x00010001)
(8 bytes) magic string = littlefs
(32 bits) CRC = 0xc50b74fa
(32 bits) crc = 0xc50b74fa
00000000: 03 00 00 00 34 00 00 00 03 00 00 00 02 00 00 00 ....4...........
00000010: 2e 14 00 08 03 00 00 00 02 00 00 00 00 02 00 00 ................

View File

@@ -1,324 +0,0 @@
/*
* Block device emulated on standard files
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "emubd/lfs2_emubd.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
#include <inttypes.h>
// Emulated block device utils
static inline void lfs2_emubd_tole32(lfs2_emubd_t *emu) {
emu->cfg.read_size = lfs2_tole32(emu->cfg.read_size);
emu->cfg.prog_size = lfs2_tole32(emu->cfg.prog_size);
emu->cfg.block_size = lfs2_tole32(emu->cfg.block_size);
emu->cfg.block_count = lfs2_tole32(emu->cfg.block_count);
emu->stats.read_count = lfs2_tole32(emu->stats.read_count);
emu->stats.prog_count = lfs2_tole32(emu->stats.prog_count);
emu->stats.erase_count = lfs2_tole32(emu->stats.erase_count);
for (unsigned i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs2_tole32(emu->history.blocks[i]);
}
}
static inline void lfs2_emubd_fromle32(lfs2_emubd_t *emu) {
emu->cfg.read_size = lfs2_fromle32(emu->cfg.read_size);
emu->cfg.prog_size = lfs2_fromle32(emu->cfg.prog_size);
emu->cfg.block_size = lfs2_fromle32(emu->cfg.block_size);
emu->cfg.block_count = lfs2_fromle32(emu->cfg.block_count);
emu->stats.read_count = lfs2_fromle32(emu->stats.read_count);
emu->stats.prog_count = lfs2_fromle32(emu->stats.prog_count);
emu->stats.erase_count = lfs2_fromle32(emu->stats.erase_count);
for (unsigned i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs2_fromle32(emu->history.blocks[i]);
}
}
// Block device emulated on existing filesystem
int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path) {
lfs2_emubd_t *emu = cfg->context;
emu->cfg.read_size = cfg->read_size;
emu->cfg.prog_size = cfg->prog_size;
emu->cfg.block_size = cfg->block_size;
emu->cfg.block_count = cfg->block_count;
// Allocate buffer for creating children files
size_t pathlen = strlen(path);
emu->path = malloc(pathlen + 1 + LFS2_NAME_MAX + 1);
if (!emu->path) {
return -ENOMEM;
}
strcpy(emu->path, path);
emu->path[pathlen] = '/';
emu->child = &emu->path[pathlen+1];
memset(emu->child, '\0', LFS2_NAME_MAX+1);
// Create directory if it doesn't exist
int err = mkdir(path, 0777);
if (err && errno != EEXIST) {
return -errno;
}
// Load stats to continue incrementing
snprintf(emu->child, LFS2_NAME_MAX, ".stats");
FILE *f = fopen(emu->path, "r");
if (!f) {
memset(&emu->stats, 0, sizeof(emu->stats));
} else {
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
lfs2_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
// Load history
snprintf(emu->child, LFS2_NAME_MAX, ".history");
f = fopen(emu->path, "r");
if (!f) {
memset(&emu->history, 0, sizeof(emu->history));
} else {
size_t res = fread(&emu->history, sizeof(emu->history), 1, f);
lfs2_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
return 0;
}
void lfs2_emubd_destroy(const struct lfs2_config *cfg) {
lfs2_emubd_sync(cfg);
lfs2_emubd_t *emu = cfg->context;
free(emu->path);
}
int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size) {
lfs2_emubd_t *emu = cfg->context;
uint8_t *data = buffer;
// Check if read is valid
assert(off % cfg->read_size == 0);
assert(size % cfg->read_size == 0);
assert(block < cfg->block_count);
// Zero out buffer for debugging
memset(data, 0, size);
// Read data
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) {
return -errno;
}
if (f) {
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fread(data, 1, size, f);
if (res < size && !feof(f)) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
emu->stats.read_count += 1;
return 0;
}
int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size) {
lfs2_emubd_t *emu = cfg->context;
const uint8_t *data = buffer;
// Check if write is valid
assert(off % cfg->prog_size == 0);
assert(size % cfg->prog_size == 0);
assert(block < cfg->block_count);
// Program data
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
FILE *f = fopen(emu->path, "r+b");
if (!f) {
return (errno == EACCES) ? 0 : -errno;
}
// Check that file was erased
assert(f);
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fwrite(data, 1, size, f);
if (res < size) {
return -errno;
}
err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
uint8_t dat;
res = fread(&dat, 1, 1, f);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
// update history and stats
if (block != emu->history.blocks[0]) {
memcpy(&emu->history.blocks[1], &emu->history.blocks[0],
sizeof(emu->history) - sizeof(emu->history.blocks[0]));
emu->history.blocks[0] = block;
}
emu->stats.prog_count += 1;
return 0;
}
int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block) {
lfs2_emubd_t *emu = cfg->context;
// Check if erase is valid
assert(block < cfg->block_count);
// Erase the block
snprintf(emu->child, LFS2_NAME_MAX, "%" PRIx32, block);
struct stat st;
int err = stat(emu->path, &st);
if (err && errno != ENOENT) {
return -errno;
}
if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) {
err = unlink(emu->path);
if (err) {
return -errno;
}
}
if (err || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) {
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
emu->stats.erase_count += 1;
return 0;
}
int lfs2_emubd_sync(const struct lfs2_config *cfg) {
lfs2_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup
snprintf(emu->child, LFS2_NAME_MAX, ".config");
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs2_emubd_tole32(emu);
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
lfs2_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
int err = fclose(f);
if (err) {
return -errno;
}
snprintf(emu->child, LFS2_NAME_MAX, ".stats");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs2_emubd_tole32(emu);
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
lfs2_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
snprintf(emu->child, LFS2_NAME_MAX, ".history");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs2_emubd_tole32(emu);
res = fwrite(&emu->history, sizeof(emu->history), 1, f);
lfs2_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
return 0;
}

View File

@@ -1,91 +0,0 @@
/*
* Block device emulated on standard files
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_EMUBD_H
#define LFS2_EMUBD_H
#include "lfs2.h"
#include "lfs2_util.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Config options
#ifndef LFS2_EMUBD_READ_SIZE
#define LFS2_EMUBD_READ_SIZE 1
#endif
#ifndef LFS2_EMUBD_PROG_SIZE
#define LFS2_EMUBD_PROG_SIZE 1
#endif
#ifndef LFS2_EMUBD_ERASE_SIZE
#define LFS2_EMUBD_ERASE_SIZE 512
#endif
#ifndef LFS2_EMUBD_TOTAL_SIZE
#define LFS2_EMUBD_TOTAL_SIZE 524288
#endif
// The emu bd state
typedef struct lfs2_emubd {
char *path;
char *child;
struct {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
} stats;
struct {
lfs2_block_t blocks[4];
} history;
struct {
uint32_t read_size;
uint32_t prog_size;
uint32_t block_size;
uint32_t block_count;
} cfg;
} lfs2_emubd_t;
// Create a block device using path for the directory to store blocks
int lfs2_emubd_create(const struct lfs2_config *cfg, const char *path);
// Clean up memory associated with emu block device
void lfs2_emubd_destroy(const struct lfs2_config *cfg);
// Read a block
int lfs2_emubd_read(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs2_emubd_prog(const struct lfs2_config *cfg, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs2_emubd_erase(const struct lfs2_config *cfg, lfs2_block_t block);
// Sync the block device
int lfs2_emubd_sync(const struct lfs2_config *cfg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

253
emubd/lfs_emubd.c Normal file
View File

@@ -0,0 +1,253 @@
/*
* Block device emulated on standard files
*
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "emubd/lfs_emubd.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <stdbool.h>
// Block device emulated on existing filesystem
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
lfs_emubd_t *emu = cfg->context;
emu->cfg.read_size = cfg->read_size;
emu->cfg.prog_size = cfg->prog_size;
emu->cfg.block_size = cfg->block_size;
emu->cfg.block_count = cfg->block_count;
// Allocate buffer for creating children files
size_t pathlen = strlen(path);
emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1);
if (!emu->path) {
return -ENOMEM;
}
strcpy(emu->path, path);
emu->path[pathlen] = '/';
emu->child = &emu->path[pathlen+1];
memset(emu->child, '\0', LFS_NAME_MAX+1);
// Create directory if it doesn't exist
int err = mkdir(path, 0777);
if (err && errno != EEXIST) {
return -errno;
}
// Load stats to continue incrementing
snprintf(emu->child, LFS_NAME_MAX, "stats");
FILE *f = fopen(emu->path, "r");
if (!f) {
return -errno;
}
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
return 0;
}
void lfs_emubd_destroy(const struct lfs_config *cfg) {
lfs_emubd_sync(cfg);
lfs_emubd_t *emu = cfg->context;
free(emu->path);
}
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
lfs_emubd_t *emu = cfg->context;
uint8_t *data = buffer;
// Check if read is valid
assert(off % cfg->read_size == 0);
assert(size % cfg->read_size == 0);
assert(block < cfg->block_count);
// Zero out buffer for debugging
memset(data, 0, size);
// Read data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
FILE *f = fopen(emu->path, "rb");
if (!f && errno != ENOENT) {
return -errno;
}
if (f) {
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fread(data, 1, size, f);
if (res < size && !feof(f)) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
emu->stats.read_count += 1;
return 0;
}
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
lfs_emubd_t *emu = cfg->context;
const uint8_t *data = buffer;
// Check if write is valid
assert(off % cfg->prog_size == 0);
assert(size % cfg->prog_size == 0);
assert(block < cfg->block_count);
// Program data
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
FILE *f = fopen(emu->path, "r+b");
if (!f) {
return (errno == EACCES) ? 0 : -errno;
}
// Check that file was erased
assert(f);
int err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
size_t res = fwrite(data, 1, size, f);
if (res < size) {
return -errno;
}
err = fseek(f, off, SEEK_SET);
if (err) {
return -errno;
}
uint8_t dat;
res = fread(&dat, 1, 1, f);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
emu->stats.prog_count += 1;
return 0;
}
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
lfs_emubd_t *emu = cfg->context;
// Check if erase is valid
assert(block < cfg->block_count);
// Erase the block
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
struct stat st;
int err = stat(emu->path, &st);
if (err && errno != ENOENT) {
return -errno;
}
if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) {
int err = unlink(emu->path);
if (err) {
return -errno;
}
}
if (errno == ENOENT || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) {
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
emu->stats.erase_count += 1;
return 0;
}
int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, "config");
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
if (res < 1) {
return -errno;
}
int err = fclose(f);
if (err) {
return -errno;
}
snprintf(emu->child, LFS_NAME_MAX, "stats");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
return 0;
}

89
emubd/lfs_emubd.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Block device emulated on standard files
*
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LFS_EMUBD_H
#define LFS_EMUBD_H
#include "lfs.h"
#include "lfs_util.h"
// Config options
#ifndef LFS_EMUBD_READ_SIZE
#define LFS_EMUBD_READ_SIZE 1
#endif
#ifndef LFS_EMUBD_PROG_SIZE
#define LFS_EMUBD_PROG_SIZE 1
#endif
#ifndef LFS_EMUBD_ERASE_SIZE
#define LFS_EMUBD_ERASE_SIZE 512
#endif
#ifndef LFS_EMUBD_TOTAL_SIZE
#define LFS_EMUBD_TOTAL_SIZE 524288
#endif
// The emu bd state
typedef struct lfs_emubd {
char *path;
char *child;
struct {
uint64_t read_count;
uint64_t prog_count;
uint64_t erase_count;
} stats;
struct {
uint32_t read_size;
uint32_t prog_size;
uint32_t block_size;
uint32_t block_count;
} cfg;
} lfs_emubd_t;
// Create a block device using path for the directory to store blocks
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
// Clean up memory associated with emu block device
void lfs_emubd_destroy(const struct lfs_config *cfg);
// Read a block
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a block
//
// The block must have previously been erased.
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block
//
// A block must be erased before being programmed. The
// state of an erased block is undefined.
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
// Sync the block device
int lfs_emubd_sync(const struct lfs_config *cfg);
#endif

2553
lfs.c Normal file

File diff suppressed because it is too large Load Diff

459
lfs.h Normal file
View File

@@ -0,0 +1,459 @@
/*
* The little filesystem
*
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LFS_H
#define LFS_H
#include <stdint.h>
#include <stdbool.h>
/// Definitions ///
// Type definitions
typedef uint32_t lfs_size_t;
typedef uint32_t lfs_off_t;
typedef int32_t lfs_ssize_t;
typedef int32_t lfs_soff_t;
typedef uint32_t lfs_block_t;
// Max name size in bytes
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 255
#endif
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -52, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
};
// File types
enum lfs_type {
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
};
// File open flags
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x80000, // An error occured during write
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
// Configuration provided during initialization of the littlefs
struct lfs_config {
// Opaque user provided context that can be used to pass
// information to the block device operations
void *context;
// Read a region in a block. Negative error codes are propogated
// to the user.
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
int (*sync)(const struct lfs_config *c);
// Minimum size of a block read. This determines the size of read buffers.
// This may be larger than the physical read size to improve performance
// by caching more of the block device.
lfs_size_t read_size;
// Minimum size of a block program. This determines the size of program
// buffers. This may be larger than the physical program size to improve
// performance by caching more of the block device.
// Must be a multiple of the read size.
lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block.
// Must be a multiple of the program size.
lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of blocks to lookahead during block allocation. A larger
// lookahead reduces the number of passes required to allocate a block.
// The lookahead buffer requires only 1 bit per block so it can be quite
// large with little ram impact. Should be a multiple of 32.
lfs_size_t lookahead;
// Optional, statically allocated read buffer. Must be read sized.
void *read_buffer;
// Optional, statically allocated program buffer. Must be program sized.
void *prog_buffer;
// Optional, statically allocated lookahead buffer. Must be 1 bit per
// lookahead block.
void *lookahead_buffer;
// Optional, statically allocated buffer for files. Must be program sized.
// If enabled, only one file may be opened at a time.
void *file_buffer;
};
// File info structure
struct lfs_info {
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
uint8_t type;
// Size of the file, only valid for REG files
lfs_size_t size;
// Name of the file stored as a null-terminated string
char name[LFS_NAME_MAX+1];
};
/// littlefs data structures ///
typedef struct lfs_entry {
lfs_off_t off;
struct lfs_disk_entry {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
union {
struct {
lfs_block_t head;
lfs_size_t size;
} file;
lfs_block_t dir[2];
} u;
} d;
} lfs_entry_t;
typedef struct lfs_cache {
lfs_block_t block;
lfs_off_t off;
uint8_t *buffer;
} lfs_cache_t;
typedef struct lfs_file {
struct lfs_file *next;
lfs_block_t pair[2];
lfs_off_t poff;
lfs_block_t head;
lfs_size_t size;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
} lfs_file_t;
typedef struct lfs_dir {
struct lfs_dir *next;
lfs_block_t pair[2];
lfs_off_t off;
lfs_block_t head[2];
lfs_off_t pos;
struct lfs_disk_dir {
uint32_t rev;
lfs_size_t size;
lfs_block_t tail[2];
} d;
} lfs_dir_t;
typedef struct lfs_superblock {
lfs_off_t off;
struct lfs_disk_superblock {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
char magic[8];
} d;
} lfs_superblock_t;
typedef struct lfs_free {
lfs_block_t begin;
lfs_block_t end;
lfs_block_t off;
uint32_t *buffer;
} lfs_free_t;
// The littlefs type
typedef struct lfs {
const struct lfs_config *cfg;
lfs_block_t root[2];
lfs_file_t *files;
lfs_dir_t *dirs;
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_free_t free;
bool deorphaned;
} lfs_t;
/// Filesystem functions ///
// Format a block device with the littlefs
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted.
//
// Returns a negative error code on failure.
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
// Mounts a littlefs
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs and config must be allocated while mounted.
//
// Returns a negative error code on failure.
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
// Unmounts a littlefs
//
// Does nothing besides releasing any allocated resources.
// Returns a negative error code on failure.
int lfs_unmount(lfs_t *lfs);
/// General operations ///
// Removes a file or directory
//
// If removing a directory, the directory must be empty.
// Returns a negative error code on failure.
int lfs_remove(lfs_t *lfs, const char *path);
// Rename or move a file or directory
//
// If the destination exists, it must match the source in type.
// If the destination is a directory, the directory must be empty.
//
// Note: If power loss occurs, it is possible that the file or directory
// will exist in both the oldpath and newpath simultaneously after the
// next mount.
//
// Returns a negative error code on failure.
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// Find info about a file or directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined
// by the flags, which are values from the enum lfs_open_flags
// that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags);
// Close a file
//
// Any pending writes are written out to storage as though
// sync had been called and releases any allocated resources.
//
// Returns a negative error code on failure.
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
// Synchronize a file on storage
//
// Any pending writes are written out to storage.
// Returns a negative error code on failure.
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
// Read data from file
//
// Takes a buffer and size indicating where to store the read data.
// Returns the number of bytes read, or a negative error code on failure.
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
void *buffer, lfs_size_t size);
// Write data to file
//
// Takes a buffer and size indicating the data to write. The file will not
// actually be updated on the storage until either sync or close is called.
//
// Returns the number of bytes written, or a negative error code on failure.
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
const void *buffer, lfs_size_t size);
// Change the position of the file
//
// The change in position is determined by the offset and whence flag.
// Returns the old position of the file, or a negative error code on failure.
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
lfs_soff_t off, int whence);
// Truncates the size of the file to the specified size
//
// Returns a negative error code on failure.
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size);
// Return the position of the file
//
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
// Returns the position of the file, or a negative error code on failure.
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
// Change the position of the file to the beginning of the file
//
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
// Returns a negative error code on failure.
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
// Return the size of the file
//
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
// Returns the size of the file, or a negative error code on failure.
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
/// Directory operations ///
// Create a directory
//
// Returns a negative error code on failure.
int lfs_mkdir(lfs_t *lfs, const char *path);
// Open a directory
//
// Once open a directory can be used with read to iterate over files.
// Returns a negative error code on failure.
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
// Close a directory
//
// Releases any allocated resources.
// Returns a negative error code on failure.
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
// Read an entry in the directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
// Change the position of the directory
//
// The new off must be a value previous returned from tell and specifies
// an absolute offset in the directory seek.
//
// Returns a negative error code on failure.
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
// Return the position of the directory
//
// The returned offset is only meant to be consumed by seek and may not make
// sense, but does indicate the current position in the directory iteration.
//
// Returns the position of the directory, or a negative error code on failure.
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
// Change the position of the directory to the beginning of the directory
//
// Returns a negative error code on failure.
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
/// Miscellaneous littlefs specific operations ///
// Traverse through all blocks in use by the filesystem
//
// The provided callback will be called with each block address that is
// currently in use by the filesystem. This can be used to determine which
// blocks are in use or how much of the storage is available.
//
// Returns a negative error code on failure.
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
// Prunes any recoverable errors that may have occured in the filesystem
//
// Not needed to be called by user unless an operation is interrupted
// but the filesystem is still mounted. This is already called on first
// allocation.
//
// Returns a negative error code on failure.
int lfs_deorphan(lfs_t *lfs);
#endif

3794
lfs2.c

File diff suppressed because it is too large Load Diff

625
lfs2.h
View File

@@ -1,625 +0,0 @@
/*
* The little filesystem
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_H
#define LFS2_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
/// Version info ///
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS2_VERSION 0x00020000
#define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16))
#define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0))
// Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS2_DISK_VERSION 0x00020000
#define LFS2_DISK_VERSION_MAJOR (0xffff & (LFS2_DISK_VERSION >> 16))
#define LFS2_DISK_VERSION_MINOR (0xffff & (LFS2_DISK_VERSION >> 0))
/// Definitions ///
// Type definitions
typedef uint32_t lfs2_size_t;
typedef uint32_t lfs2_off_t;
typedef int32_t lfs2_ssize_t;
typedef int32_t lfs2_soff_t;
typedef uint32_t lfs2_block_t;
// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= 1022. Stored in superblock and must be
// respected by other littlefs drivers.
#ifndef LFS2_NAME_MAX
#define LFS2_NAME_MAX 255
#endif
// Maximum size of a file in bytes, may be redefined to limit to support other
// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the
// functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return
// incorrect values due to using signed integers. Stored in superblock and
// must be respected by other littlefs drivers.
#ifndef LFS2_FILE_MAX
#define LFS2_FILE_MAX 2147483647
#endif
// Maximum size of custom attributes in bytes, may be redefined, but there is
// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022.
#ifndef LFS2_ATTR_MAX
#define LFS2_ATTR_MAX 1022
#endif
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs2_error {
LFS2_ERR_OK = 0, // No error
LFS2_ERR_IO = -5, // Error during device operation
LFS2_ERR_CORRUPT = -84, // Corrupted
LFS2_ERR_NOENT = -2, // No directory entry
LFS2_ERR_EXIST = -17, // Entry already exists
LFS2_ERR_NOTDIR = -20, // Entry is not a dir
LFS2_ERR_ISDIR = -21, // Entry is a dir
LFS2_ERR_NOTEMPTY = -39, // Dir is not empty
LFS2_ERR_BADF = -9, // Bad file number
LFS2_ERR_FBIG = -27, // File too large
LFS2_ERR_INVAL = -22, // Invalid parameter
LFS2_ERR_NOSPC = -28, // No space left on device
LFS2_ERR_NOMEM = -12, // No more memory available
LFS2_ERR_NOATTR = -61, // No data/attr available
LFS2_ERR_NAMETOOLONG = -36, // File name too long
};
// File types
enum lfs2_type {
// file types
LFS2_TYPE_REG = 0x001,
LFS2_TYPE_DIR = 0x002,
// internally used types
LFS2_TYPE_SPLICE = 0x400,
LFS2_TYPE_NAME = 0x000,
LFS2_TYPE_STRUCT = 0x200,
LFS2_TYPE_USERATTR = 0x300,
LFS2_TYPE_FROM = 0x100,
LFS2_TYPE_TAIL = 0x600,
LFS2_TYPE_GLOBALS = 0x700,
LFS2_TYPE_CRC = 0x500,
// internally used type specializations
LFS2_TYPE_CREATE = 0x401,
LFS2_TYPE_DELETE = 0x4ff,
LFS2_TYPE_SUPERBLOCK = 0x0ff,
LFS2_TYPE_DIRSTRUCT = 0x200,
LFS2_TYPE_CTZSTRUCT = 0x202,
LFS2_TYPE_INLINESTRUCT = 0x201,
LFS2_TYPE_SOFTTAIL = 0x600,
LFS2_TYPE_HARDTAIL = 0x601,
LFS2_TYPE_MOVESTATE = 0x7ff,
// internal chip sources
LFS2_FROM_NOOP = 0x000,
LFS2_FROM_MOVE = 0x101,
LFS2_FROM_USERATTRS = 0x102,
};
// File open flags
enum lfs2_open_flags {
// open flags
LFS2_O_RDONLY = 1, // Open a file as read only
LFS2_O_WRONLY = 2, // Open a file as write only
LFS2_O_RDWR = 3, // Open a file as read and write
LFS2_O_CREAT = 0x0100, // Create a file if it does not exist
LFS2_O_EXCL = 0x0200, // Fail if a file already exists
LFS2_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS2_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS2_F_DIRTY = 0x010000, // File does not match storage
LFS2_F_WRITING = 0x020000, // File has been written since last flush
LFS2_F_READING = 0x040000, // File has been read since last flush
LFS2_F_ERRED = 0x080000, // An error occured during write
LFS2_F_INLINE = 0x100000, // Currently inlined in directory entry
};
// File seek flags
enum lfs2_whence_flags {
LFS2_SEEK_SET = 0, // Seek relative to an absolute position
LFS2_SEEK_CUR = 1, // Seek relative to the current file position
LFS2_SEEK_END = 2, // Seek relative to the end of the file
};
// Configuration provided during initialization of the littlefs
struct lfs2_config {
// Opaque user provided context that can be used to pass
// information to the block device operations
void *context;
// Read a region in a block. Negative error codes are propogated
// to the user.
int (*read)(const struct lfs2_config *c, lfs2_block_t block,
lfs2_off_t off, void *buffer, lfs2_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS2_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs2_config *c, lfs2_block_t block,
lfs2_off_t off, const void *buffer, lfs2_size_t size);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS2_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs2_config *c, lfs2_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
int (*sync)(const struct lfs2_config *c);
// Minimum size of a block read. All read operations will be a
// multiple of this value.
lfs2_size_t read_size;
// Minimum size of a block program. All program operations will be a
// multiple of this value.
lfs2_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, non-inlined files
// take up at minimum one block. Must be a multiple of the read
// and program sizes.
lfs2_size_t block_size;
// Number of erasable blocks on the device.
lfs2_size_t block_count;
// Number of erase cycles before we should move data to another block.
// May be zero, in which case no block-level wear-leveling is performed.
uint32_t block_cycles;
// Size of block caches. Each cache buffers a portion of a block in RAM.
// The littlefs needs a read cache, a program cache, and one additional
// cache per file. Larger caches can improve performance by storing more
// data and reducing the number of disk accesses. Must be a multiple of
// the read and program sizes, and a factor of the block size.
lfs2_size_t cache_size;
// Size of the lookahead buffer in bytes. A larger lookahead buffer
// increases the number of blocks found during an allocation pass. The
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
// can track 8 blocks. Must be a multiple of 4.
lfs2_size_t lookahead_size;
// Optional statically allocated read buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *read_buffer;
// Optional statically allocated program buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *prog_buffer;
// Optional statically allocated program buffer. Must be lookahead_size.
// By default lfs2_malloc is used to allocate this buffer.
void *lookahead_buffer;
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs2_size_t name_max;
// Optional upper limit on files in bytes. No downside for larger files
// but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored
// in superblock and must be respected by other littlefs drivers.
lfs2_size_t file_max;
// Optional upper limit on custom attributes in bytes. No downside for
// larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to
// LFS2_ATTR_MAX when zero.
lfs2_size_t attr_max;
};
// File info structure
struct lfs2_info {
// Type of the file, either LFS2_TYPE_REG or LFS2_TYPE_DIR
uint8_t type;
// Size of the file, only valid for REG files. Limited to 32-bits.
lfs2_size_t size;
// Name of the file stored as a null-terminated string. Limited to
// LFS2_NAME_MAX+1, which can be changed by redefining LFS2_NAME_MAX to
// reduce RAM. LFS2_NAME_MAX is stored in superblock and must be
// respected by other littlefs drivers.
char name[LFS2_NAME_MAX+1];
};
// Custom attribute structure, used to describe custom attributes
// committed atomically during file writes.
struct lfs2_attr {
// 8-bit type of attribute, provided by user and used to
// identify the attribute
uint8_t type;
// Pointer to buffer containing the attribute
void *buffer;
// Size of attribute in bytes, limited to LFS2_ATTR_MAX
lfs2_size_t size;
};
// Optional configuration provided during lfs2_file_opencfg
struct lfs2_file_config {
// Optional statically allocated file buffer. Must be cache_size.
// By default lfs2_malloc is used to allocate this buffer.
void *buffer;
// Optional list of custom attributes related to the file. If the file
// is opened with read access, these attributes will be read from disk
// during the open call. If the file is opened with write access, the
// attributes will be written to disk every file sync or close. This
// write occurs atomically with update to the file's contents.
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller
// than the buffer, it will be padded with zeros. If the stored attribute
// is larger, then it will be silently truncated. If the attribute is not
// found, it will be created implicitly.
struct lfs2_attr *attrs;
// Number of custom attributes in the list
lfs2_size_t attr_count;
};
/// internal littlefs data structures ///
typedef struct lfs2_cache {
lfs2_block_t block;
lfs2_off_t off;
lfs2_size_t size;
uint8_t *buffer;
} lfs2_cache_t;
typedef struct lfs2_mdir {
lfs2_block_t pair[2];
uint32_t rev;
lfs2_off_t off;
uint32_t etag;
uint16_t count;
bool erased;
bool split;
lfs2_block_t tail[2];
} lfs2_mdir_t;
// littlefs directory type
typedef struct lfs2_dir {
struct lfs2_dir *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
lfs2_off_t pos;
lfs2_block_t head[2];
} lfs2_dir_t;
// littlefs file type
typedef struct lfs2_file {
struct lfs2_file *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
struct lfs2_ctz {
lfs2_block_t head;
lfs2_size_t size;
} ctz;
uint32_t flags;
lfs2_off_t pos;
lfs2_block_t block;
lfs2_off_t off;
lfs2_cache_t cache;
const struct lfs2_file_config *cfg;
} lfs2_file_t;
typedef struct lfs2_superblock {
uint32_t version;
lfs2_size_t block_size;
lfs2_size_t block_count;
lfs2_size_t name_max;
lfs2_size_t file_max;
lfs2_size_t attr_max;
} lfs2_superblock_t;
// The littlefs filesystem type
typedef struct lfs2 {
lfs2_cache_t rcache;
lfs2_cache_t pcache;
lfs2_block_t root[2];
struct lfs2_mlist {
struct lfs2_mlist *next;
uint16_t id;
uint8_t type;
lfs2_mdir_t m;
} *mlist;
uint32_t seed;
struct lfs2_gstate {
uint32_t tag;
lfs2_block_t pair[2];
} gstate, gpending, gdelta;
struct lfs2_free {
lfs2_block_t off;
lfs2_block_t size;
lfs2_block_t i;
lfs2_block_t ack;
uint32_t *buffer;
} free;
const struct lfs2_config *cfg;
lfs2_size_t name_max;
lfs2_size_t file_max;
lfs2_size_t attr_max;
} lfs2_t;
/// Filesystem functions ///
// Format a block device with the littlefs
//
// Requires a littlefs object and config struct. This clobbers the littlefs
// object, and does not leave the filesystem mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *config);
// Mounts a littlefs
//
// Requires a littlefs object and config struct. Multiple filesystems
// may be mounted simultaneously with multiple littlefs objects. Both
// lfs2 and config must be allocated while mounted. The config struct must
// be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *config);
// Unmounts a littlefs
//
// Does nothing besides releasing any allocated resources.
// Returns a negative error code on failure.
int lfs2_unmount(lfs2_t *lfs2);
/// General operations ///
// Removes a file or directory
//
// If removing a directory, the directory must be empty.
// Returns a negative error code on failure.
int lfs2_remove(lfs2_t *lfs2, const char *path);
// Rename or move a file or directory
//
// If the destination exists, it must match the source in type.
// If the destination is a directory, the directory must be empty.
//
// Returns a negative error code on failure.
int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath);
// Find info about a file or directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. When read, if the stored attribute is smaller than
// the buffer, it will be padded with zeros. If the stored attribute is larger,
// then it will be silently truncated. If no attribute is found, the error
// LFS2_ERR_NOATTR is returned and the buffer is filled with zeros.
//
// Returns the size of the attribute, or a negative error code on failure.
// Note, the returned size is the size of the attribute on disk, irrespective
// of the size of the buffer. This can be used to dynamically allocate a buffer
// or check for existance.
lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path,
uint8_t type, void *buffer, lfs2_size_t size);
// Set custom attributes
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS2_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created.
//
// Returns a negative error code on failure.
int lfs2_setattr(lfs2_t *lfs2, const char *path,
uint8_t type, const void *buffer, lfs2_size_t size);
// Removes a custom attribute
//
// If an attribute is not found, nothing happens.
//
// Returns a negative error code on failure.
int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type);
/// File operations ///
// Open a file
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs2_open_flags that are bitwise-ored together.
//
// Returns a negative error code on failure.
int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags);
// Open a file with extra configuration
//
// The mode that the file is opened in is determined by the flags, which
// are values from the enum lfs2_open_flags that are bitwise-ored together.
//
// The config struct provides additional config options per file as described
// above. The config struct must be allocated while the file is open, and the
// config struct must be zeroed for defaults and backwards compatibility.
//
// Returns a negative error code on failure.
int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file,
const char *path, int flags,
const struct lfs2_file_config *config);
// Close a file
//
// Any pending writes are written out to storage as though
// sync had been called and releases any allocated resources.
//
// Returns a negative error code on failure.
int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file);
// Synchronize a file on storage
//
// Any pending writes are written out to storage.
// Returns a negative error code on failure.
int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file);
// Read data from file
//
// Takes a buffer and size indicating where to store the read data.
// Returns the number of bytes read, or a negative error code on failure.
lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file,
void *buffer, lfs2_size_t size);
// Write data to file
//
// Takes a buffer and size indicating the data to write. The file will not
// actually be updated on the storage until either sync or close is called.
//
// Returns the number of bytes written, or a negative error code on failure.
lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file,
const void *buffer, lfs2_size_t size);
// Change the position of the file
//
// The change in position is determined by the offset and whence flag.
// Returns the old position of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file,
lfs2_soff_t off, int whence);
// Truncates the size of the file to the specified size
//
// Returns a negative error code on failure.
int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size);
// Return the position of the file
//
// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR)
// Returns the position of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file);
// Change the position of the file to the beginning of the file
//
// Equivalent to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_CUR)
// Returns a negative error code on failure.
int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file);
// Return the size of the file
//
// Similar to lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END)
// Returns the size of the file, or a negative error code on failure.
lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file);
/// Directory operations ///
// Create a directory
//
// Returns a negative error code on failure.
int lfs2_mkdir(lfs2_t *lfs2, const char *path);
// Open a directory
//
// Once open a directory can be used with read to iterate over files.
// Returns a negative error code on failure.
int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path);
// Close a directory
//
// Releases any allocated resources.
// Returns a negative error code on failure.
int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir);
// Read an entry in the directory
//
// Fills out the info structure, based on the specified file or directory.
// Returns a negative error code on failure.
int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info);
// Change the position of the directory
//
// The new off must be a value previous returned from tell and specifies
// an absolute offset in the directory seek.
//
// Returns a negative error code on failure.
int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off);
// Return the position of the directory
//
// The returned offset is only meant to be consumed by seek and may not make
// sense, but does indicate the current position in the directory iteration.
//
// Returns the position of the directory, or a negative error code on failure.
lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir);
// Change the position of the directory to the beginning of the directory
//
// Returns a negative error code on failure.
int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir);
/// Filesystem-level filesystem operations
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned
// size may be larger than the filesystem actually is.
//
// Returns the number of allocated blocks, or a negative error code on failure.
lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2);
// Traverse through all blocks in use by the filesystem
//
// The provided callback will be called with each block address that is
// currently in use by the filesystem. This can be used to determine which
// blocks are in use or how much of the storage is available.
//
// Returns a negative error code on failure.
int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -1,33 +0,0 @@
/*
* lfs2 util functions
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "lfs2_util.h"
// Only compile if user does not provide custom config
#ifndef LFS2_CONFIG
// Software CRC implementation with small lookup table
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
const uint8_t *data = buffer;
for (size_t i = 0; i < size; i++) {
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
}
return crc;
}
#endif

View File

@@ -1,219 +0,0 @@
/*
* lfs2 utility functions
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LFS2_UTIL_H
#define LFS2_UTIL_H
// Users can override lfs2_util.h with their own configuration by defining
// LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h).
//
// If LFS2_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start, I would suggest copying lfs2_util.h
// and modifying as needed.
#ifdef LFS2_CONFIG
#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x)
#define LFS2_STRINGIZE2(x) #x
#include LFS2_STRINGIZE(LFS2_CONFIG)
#else
// System includes
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#ifndef LFS2_NO_MALLOC
#include <stdlib.h>
#endif
#ifndef LFS2_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS2_NO_DEBUG) || !defined(LFS2_NO_WARN) || !defined(LFS2_NO_ERROR)
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
// Macros, may be replaced by system specific wrappers. Arguments to these
// macros must not have side-effects as the macros can be removed for a smaller
// code footprint
// Logging functions
#ifndef LFS2_NO_DEBUG
#define LFS2_DEBUG(fmt, ...) \
printf("lfs2 debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS2_DEBUG(fmt, ...)
#endif
#ifndef LFS2_NO_WARN
#define LFS2_WARN(fmt, ...) \
printf("lfs2 warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS2_WARN(fmt, ...)
#endif
#ifndef LFS2_NO_ERROR
#define LFS2_ERROR(fmt, ...) \
printf("lfs2 error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS2_ERROR(fmt, ...)
#endif
// Runtime assertions
#ifndef LFS2_NO_ASSERT
#define LFS2_ASSERT(test) assert(test)
#else
#define LFS2_ASSERT(test)
#endif
// Builtin functions, these may be replaced by more efficient
// toolchain-specific implementations. LFS2_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs2_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
static inline uint32_t lfs2_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
// Align to nearest multiple of a size
static inline uint32_t lfs2_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);
}
static inline uint32_t lfs2_alignup(uint32_t a, uint32_t alignment) {
return lfs2_aligndown(a + alignment-1, alignment);
}
// Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs2_npw2(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
#else
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4; a >>= s; r |= s;
s = (a > 0xff ) << 3; a >>= s; r |= s;
s = (a > 0xf ) << 2; a >>= s; r |= s;
s = (a > 0x3 ) << 1; a >>= s; r |= s;
return (r | (a >> 1)) + 1;
#endif
}
// Count the number of trailing binary zeros in a
// lfs2_ctz(0) may be undefined
static inline uint32_t lfs2_ctz(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
#else
return lfs2_npw2((a & -a) + 1) - 1;
#endif
}
// Count the number of binary ones in a
static inline uint32_t lfs2_popc(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
#endif
}
// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs2_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs2_fromle32(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8) |
(((uint8_t*)&a)[2] << 16) |
(((uint8_t*)&a)[3] << 24);
#endif
}
static inline uint32_t lfs2_tole32(uint32_t a) {
return lfs2_fromle32(a);
}
// Convert between 32-bit big-endian and native order
static inline uint32_t lfs2_frombe32(uint32_t a) {
#if !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return __builtin_bswap32(a);
#elif !defined(LFS2_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return a;
#else
return (((uint8_t*)&a)[0] << 24) |
(((uint8_t*)&a)[1] << 16) |
(((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
#endif
}
static inline uint32_t lfs2_tobe32(uint32_t a) {
return lfs2_frombe32(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs2_malloc(size_t size) {
#ifndef LFS2_NO_MALLOC
return malloc(size);
#else
(void)size;
return NULL;
#endif
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs2_free(void *p) {
#ifndef LFS2_NO_MALLOC
free(p);
#else
(void)p;
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
#endif

36
lfs_util.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* lfs util functions
*
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lfs_util.h"
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
const uint8_t *data = buffer;
for (size_t i = 0; i < size; i++) {
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
}
}

80
lfs_util.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* lfs utility functions
*
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LFS_UTIL_H
#define LFS_UTIL_H
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
// Builtin functions, these may be replaced by more
// efficient implementations in the system
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
static inline uint32_t lfs_ctz(uint32_t a) {
return __builtin_ctz(a);
}
static inline uint32_t lfs_npw2(uint32_t a) {
return 32 - __builtin_clz(a-1);
}
static inline uint32_t lfs_popc(uint32_t a) {
return __builtin_popcount(a);
}
static inline int lfs_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}
static inline uint32_t lfs_fromle32(uint32_t a) {
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return a;
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return __builtin_bswap32(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8) |
(((uint8_t*)&a)[2] << 16) |
(((uint8_t*)&a)[3] << 24);
#endif
}
static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
}
// CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
// Logging functions, these may be replaced by system-specific
// logging functions
#define LFS_DEBUG(fmt, ...) printf("lfs debug: " fmt "\n", __VA_ARGS__)
#define LFS_WARN(fmt, ...) printf("lfs warn: " fmt "\n", __VA_ARGS__)
#define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__)
#endif

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env python
# This script replaces prefixes of files, and symbols in that file.
# Useful for creating different versions of the codebase that don't
# conflict at compile time.
#
# example:
# $ ./scripts/prefix.py lfs22
import os
import os.path
import re
import glob
import itertools
import tempfile
import shutil
import subprocess
DEFAULT_PREFIX = "lfs2"
def subn(from_prefix, to_prefix, name):
name, count1 = re.subn('\\b'+from_prefix, to_prefix, name)
name, count2 = re.subn('\\b'+from_prefix.upper(), to_prefix.upper(), name)
name, count3 = re.subn('\\B-D'+from_prefix.upper(),
'-D'+to_prefix.upper(), name)
return name, count1+count2+count3
def main(from_prefix, to_prefix=None, files=None):
if not to_prefix:
from_prefix, to_prefix = DEFAULT_PREFIX, from_prefix
if not files:
files = subprocess.check_output([
'git', 'ls-tree', '-r', '--name-only', 'HEAD']).split()
for oldname in files:
# Rename any matching file names
newname, namecount = subn(from_prefix, to_prefix, oldname)
if namecount:
subprocess.check_call(['git', 'mv', oldname, newname])
# Rename any prefixes in file
count = 0
with open(newname+'~', 'w') as tempf:
with open(newname) as newf:
for line in newf:
line, n = subn(from_prefix, to_prefix, line)
count += n
tempf.write(line)
shutil.copystat(newname, newname+'~')
os.rename(newname+'~', newname)
subprocess.check_call(['git', 'add', newname])
# Summary
print '%s: %d replacements' % (
'%s -> %s' % (oldname, newname) if namecount else oldname,
count)
if __name__ == "__main__":
import sys
sys.exit(main(*sys.argv[1:]))

View File

@@ -1,44 +0,0 @@
#!/usr/bin/env python
import struct
import sys
import os
import argparse
def corrupt(block):
with open(block, 'r+b') as file:
# skip rev
file.read(4)
# go to last commit
tag = 0xffffffff
while True:
try:
ntag, = struct.unpack('>I', file.read(4))
except struct.error:
break
tag ^= ntag
size = (tag & 0x3ff) if (tag & 0x3ff) != 0x3ff else 0
file.seek(size, os.SEEK_CUR)
# lob off last 3 bytes
file.seek(-(size + 3), os.SEEK_CUR)
file.truncate()
def main(args):
if args.n or not args.blocks:
with open('blocks/.history', 'rb') as file:
for i in range(int(args.n or 1)):
last, = struct.unpack('<I', file.read(4))
args.blocks.append('blocks/%x' % last)
for block in args.blocks:
print 'corrupting %s' % block
corrupt(block)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-n')
parser.add_argument('blocks', nargs='*')
main(parser.parse_args())

View File

@@ -1,112 +0,0 @@
#!/usr/bin/env python2
import struct
import binascii
TYPES = {
(0x700, 0x400): 'splice',
(0x7ff, 0x401): 'create',
(0x7ff, 0x4ff): 'delete',
(0x700, 0x000): 'name',
(0x7ff, 0x001): 'name reg',
(0x7ff, 0x002): 'name dir',
(0x7ff, 0x0ff): 'name superblock',
(0x700, 0x200): 'struct',
(0x7ff, 0x200): 'struct dir',
(0x7ff, 0x202): 'struct ctz',
(0x7ff, 0x201): 'struct inline',
(0x700, 0x300): 'userattr',
(0x700, 0x600): 'tail',
(0x7ff, 0x600): 'tail soft',
(0x7ff, 0x601): 'tail hard',
(0x700, 0x700): 'gstate',
(0x7ff, 0x7ff): 'gstate move',
(0x700, 0x500): 'crc',
}
def typeof(type):
for prefix in range(12):
mask = 0x7ff & ~((1 << prefix)-1)
if (mask, type & mask) in TYPES:
return TYPES[mask, type & mask] + (
' %0*x' % (prefix/4, type & ((1 << prefix)-1))
if prefix else '')
else:
return '%02x' % type
def main(*blocks):
# find most recent block
file = None
rev = None
crc = None
versions = []
for block in blocks:
try:
nfile = open(block, 'rb')
ndata = nfile.read(4)
ncrc = binascii.crc32(ndata)
nrev, = struct.unpack('<I', ndata)
assert rev != nrev
if not file or ((rev - nrev) & 0x80000000):
file = nfile
rev = nrev
crc = ncrc
versions.append((nrev, '%s (rev %d)' % (block, nrev)))
except (IOError, struct.error):
pass
if not file:
print 'Bad metadata pair {%s}' % ', '.join(blocks)
return 1
print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True))
# go through each tag, print useful information
print "%-4s %-8s %-14s %3s %4s %s" % (
'off', 'tag', 'type', 'id', 'len', 'dump')
tag = 0xffffffff
off = 4
while True:
try:
data = file.read(4)
crc = binascii.crc32(data, crc)
ntag, = struct.unpack('>I', data)
except struct.error:
break
tag ^= ntag
off += 4
type = (tag & 0x7ff00000) >> 20
id = (tag & 0x000ffc00) >> 10
size = (tag & 0x000003ff) >> 0
iscrc = (type & 0x700) == 0x500
data = file.read(size if size != 0x3ff else 0)
if iscrc:
crc = binascii.crc32(data[:4], crc)
else:
crc = binascii.crc32(data, crc)
print '%04x: %08x %-15s %3s %4s %-23s %-8s' % (
off, tag,
typeof(type) + (' bad!' if iscrc and ~crc else ''),
id if id != 0x3ff else '.',
size if size != 0x3ff else 'x',
' '.join('%02x' % ord(c) for c in data[:8]),
''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8]))
off += size if size != 0x3ff else 0
if iscrc:
crc = 0
tag ^= (type & 1) << 31
return 0
if __name__ == "__main__":
import sys
sys.exit(main(*sys.argv[1:]))

View File

@@ -7,7 +7,7 @@ import os
import re
def main():
with open('blocks/.config') as file:
with open('blocks/config') as file:
s = struct.unpack('<LLLL', file.read())
print 'read_size: %d' % s[0]
print 'prog_size: %d' % s[1]
@@ -18,7 +18,7 @@ def main():
os.path.getsize(os.path.join('blocks', f))
for f in os.listdir('blocks') if re.match('\d+', f))
with open('blocks/.stats') as file:
with open('blocks/stats') as file:
s = struct.unpack('<QQQ', file.read())
print 'read_count: %d' % s[0]
print 'prog_count: %d' % s[1]

View File

@@ -1,17 +1,17 @@
/// AUTOGENERATED TEST ///
#include "lfs2.h"
#include "emubd/lfs2_emubd.h"
#include "lfs.h"
#include "emubd/lfs_emubd.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// test stuff
static void test_log(const char *s, uintmax_t v) {{
void test_log(const char *s, uintmax_t v) {{
printf("%s: %jd\n", s, v);
}}
static void test_assert(const char *file, unsigned line,
void test_assert(const char *file, unsigned line,
const char *s, uintmax_t v, uintmax_t e) {{
static const char *last[6] = {{0, 0}};
if (v != e || !(last[0] == s || last[1] == s ||
@@ -37,80 +37,69 @@ static void test_assert(const char *file, unsigned line,
// utility functions for traversals
static int __attribute__((used)) test_count(void *p, lfs2_block_t b) {{
(void)b;
int test_count(void *p, lfs_block_t b) {{
unsigned *u = (unsigned*)p;
*u += 1;
return 0;
}}
// lfs2 declarations
lfs2_t lfs2;
lfs2_emubd_t bd;
lfs2_file_t file[4];
lfs2_dir_t dir[4];
struct lfs2_info info;
// lfs declarations
lfs_t lfs;
lfs_emubd_t bd;
lfs_file_t file[4];
lfs_dir_t dir[4];
struct lfs_info info;
uint8_t buffer[1024];
uint8_t wbuffer[1024];
uint8_t rbuffer[1024];
lfs2_size_t size;
lfs2_size_t wsize;
lfs2_size_t rsize;
lfs_size_t size;
lfs_size_t wsize;
lfs_size_t rsize;
uintmax_t test;
uintmax_t res;
#ifndef LFS2_READ_SIZE
#define LFS2_READ_SIZE 16
#ifndef LFS_READ_SIZE
#define LFS_READ_SIZE 16
#endif
#ifndef LFS2_PROG_SIZE
#define LFS2_PROG_SIZE LFS2_READ_SIZE
#ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE 16
#endif
#ifndef LFS2_BLOCK_SIZE
#define LFS2_BLOCK_SIZE 512
#ifndef LFS_BLOCK_SIZE
#define LFS_BLOCK_SIZE 512
#endif
#ifndef LFS2_BLOCK_COUNT
#define LFS2_BLOCK_COUNT 1024
#ifndef LFS_BLOCK_COUNT
#define LFS_BLOCK_COUNT 1024
#endif
#ifndef LFS2_BLOCK_CYCLES
#define LFS2_BLOCK_CYCLES 1024
#ifndef LFS_LOOKAHEAD
#define LFS_LOOKAHEAD 128
#endif
#ifndef LFS2_CACHE_SIZE
#define LFS2_CACHE_SIZE 64
#endif
#ifndef LFS2_LOOKAHEAD_SIZE
#define LFS2_LOOKAHEAD_SIZE 16
#endif
const struct lfs2_config cfg = {{
const struct lfs_config cfg = {{
.context = &bd,
.read = &lfs2_emubd_read,
.prog = &lfs2_emubd_prog,
.erase = &lfs2_emubd_erase,
.sync = &lfs2_emubd_sync,
.read = &lfs_emubd_read,
.prog = &lfs_emubd_prog,
.erase = &lfs_emubd_erase,
.sync = &lfs_emubd_sync,
.read_size = LFS2_READ_SIZE,
.prog_size = LFS2_PROG_SIZE,
.block_size = LFS2_BLOCK_SIZE,
.block_count = LFS2_BLOCK_COUNT,
.block_cycles = LFS2_BLOCK_CYCLES,
.cache_size = LFS2_CACHE_SIZE,
.lookahead_size = LFS2_LOOKAHEAD_SIZE,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.lookahead = LFS_LOOKAHEAD,
}};
// Entry point
int main(void) {{
lfs2_emubd_create(&cfg, "blocks");
int main() {{
lfs_emubd_create(&cfg, "blocks");
{tests}
lfs2_emubd_destroy(&cfg);
lfs_emubd_destroy(&cfg);
}}

View File

@@ -10,38 +10,26 @@ def generate(test):
template = file.read()
lines = []
for line in re.split('(?<=(?:.;| [{}]))\n', test.read()):
for line in re.split('(?<=[;{}])\n', test.read()):
match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE)
if match:
tab, test, expect = match.groups()
lines.append(tab+'test = {test};'.format(test=test.strip()))
lines.append(tab+'test_assert("{name}", test, {expect});'.format(
lines.append(tab+'res = {test};'.format(test=test.strip()))
lines.append(tab+'test_assert("{name}", res, {expect});'.format(
name = re.match('\w*', test.strip()).group(),
expect = expect.strip()))
else:
lines.append(line)
# Create test file
with open('test.c', 'w') as file:
file.write(template.format(tests='\n'.join(lines)))
# Remove build artifacts to force rebuild
try:
os.remove('test.o')
os.remove('lfs2')
except OSError:
pass
def compile():
subprocess.check_call([
os.environ.get('MAKE', 'make'),
'--no-print-directory', '-s'])
os.environ['CFLAGS'] = os.environ.get('CFLAGS', '') + ' -Werror'
subprocess.check_call(['make', '--no-print-directory', '-s'], env=os.environ)
def execute():
if 'EXEC' in os.environ:
subprocess.check_call([os.environ['EXEC'], "./lfs2"])
else:
subprocess.check_call(["./lfs2"])
subprocess.check_call(["./lfs"])
def main(test=None):
if test and not test.startswith('-'):

View File

@@ -4,482 +4,268 @@ set -eu
echo "=== Allocator tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
SIZE=15000
lfs2_mkdir() {
lfs_mkdir() {
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "$1") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "$1") => 0;
lfs_unmount(&lfs) => 0;
TEST
}
lfs2_remove() {
lfs_remove() {
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "$1/eggs") => 0;
lfs2_remove(&lfs2, "$1/bacon") => 0;
lfs2_remove(&lfs2, "$1/pancakes") => 0;
lfs2_remove(&lfs2, "$1") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "$1/eggs") => 0;
lfs_remove(&lfs, "$1/bacon") => 0;
lfs_remove(&lfs, "$1/pancakes") => 0;
lfs_remove(&lfs, "$1") => 0;
lfs_unmount(&lfs) => 0;
TEST
}
lfs2_alloc_singleproc() {
lfs_alloc_singleproc() {
tests/test.py << TEST
const char *names[] = {"bacon", "eggs", "pancakes"};
lfs2_mount(&lfs2, &cfg) => 0;
for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
lfs_mount(&lfs, &cfg) => 0;
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
sprintf((char*)buffer, "$1/%s", names[n]);
lfs2_file_open(&lfs2, &file[n], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_file_open(&lfs, &file[n], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
}
for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
size = strlen(names[n]);
for (int i = 0; i < $SIZE; i++) {
lfs2_file_write(&lfs2, &file[n], names[n], size) => size;
lfs_file_write(&lfs, &file[n], names[n], size) => size;
}
}
for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
lfs2_file_close(&lfs2, &file[n]) => 0;
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
lfs_file_close(&lfs, &file[n]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
lfs2_alloc_multiproc() {
lfs_alloc_multiproc() {
for name in bacon eggs pancakes
do
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "$1/$name",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("$name");
memcpy(buffer, "$name", size);
for (int i = 0; i < $SIZE; i++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
done
}
lfs2_verify() {
lfs_verify() {
for name in bacon eggs pancakes
do
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "$1/$name", LFS2_O_RDONLY) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0;
size = strlen("$name");
for (int i = 0; i < $SIZE; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "$name", size) => 0;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
done
}
echo "--- Single-process allocation test ---"
lfs2_mkdir singleproc
lfs2_alloc_singleproc singleproc
lfs2_verify singleproc
lfs_mkdir singleproc
lfs_alloc_singleproc singleproc
lfs_verify singleproc
echo "--- Multi-process allocation test ---"
lfs2_mkdir multiproc
lfs2_alloc_multiproc multiproc
lfs2_verify multiproc
lfs2_verify singleproc
lfs_mkdir multiproc
lfs_alloc_multiproc multiproc
lfs_verify multiproc
lfs_verify singleproc
echo "--- Single-process reuse test ---"
lfs2_remove singleproc
lfs2_mkdir singleprocreuse
lfs2_alloc_singleproc singleprocreuse
lfs2_verify singleprocreuse
lfs2_verify multiproc
lfs_remove singleproc
lfs_mkdir singleprocreuse
lfs_alloc_singleproc singleprocreuse
lfs_verify singleprocreuse
lfs_verify multiproc
echo "--- Multi-process reuse test ---"
lfs2_remove multiproc
lfs2_mkdir multiprocreuse
lfs2_alloc_singleproc multiprocreuse
lfs2_verify multiprocreuse
lfs2_verify singleprocreuse
lfs_remove multiproc
lfs_mkdir multiprocreuse
lfs_alloc_singleproc multiprocreuse
lfs_verify multiprocreuse
lfs_verify singleprocreuse
echo "--- Cleanup ---"
lfs2_remove multiprocreuse
lfs2_remove singleprocreuse
lfs_remove multiprocreuse
lfs_remove singleprocreuse
echo "--- Exhaustion test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("exhaustion");
memcpy(buffer, "exhaustion", size);
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs2_file_sync(&lfs2, &file[0]) => 0;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs_file_sync(&lfs, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_ssize_t res;
lfs_ssize_t res;
while (true) {
res = lfs2_file_write(&lfs2, &file[0], buffer, size);
res = lfs_file_write(&lfs, &file[0], buffer, size);
if (res < 0) {
break;
}
res => size;
}
res => LFS2_ERR_NOSPC;
res => LFS_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_RDONLY);
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
size = strlen("exhaustion");
lfs2_file_size(&lfs2, &file[0]) => size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_size(&lfs, &file[0]) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Exhaustion wraparound test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs2_file_open(&lfs2, &file[0], "padding", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file[0], "padding", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("buffering");
memcpy(buffer, "buffering", size);
for (int i = 0; i < $SIZE; i++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_remove(&lfs2, "padding") => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "padding") => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("exhaustion");
memcpy(buffer, "exhaustion", size);
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs2_file_sync(&lfs2, &file[0]) => 0;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs_file_sync(&lfs, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_ssize_t res;
lfs_ssize_t res;
while (true) {
res = lfs2_file_write(&lfs2, &file[0], buffer, size);
res = lfs_file_write(&lfs, &file[0], buffer, size);
if (res < 0) {
break;
}
res => size;
}
res => LFS2_ERR_NOSPC;
res => LFS_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_RDONLY);
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
size = strlen("exhaustion");
lfs2_file_size(&lfs2, &file[0]) => size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_size(&lfs, &file[0]) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dir exhaustion test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
int count = 0;
int err;
while (true) {
err = lfs2_file_write(&lfs2, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
for (lfs_size_t i = 0;
i < (cfg.block_count-6)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
err => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
// see if dir fits with max file size
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
for (int i = 0; i < count; i++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
// see if dir fits with > max file size
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Chained dir exhaustion test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size
lfs2_mkdir(&lfs2, "exhaustiondir") => 0;
for (int i = 0; i < 10; i++) {
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-24)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
int count = 0;
int err;
while (true) {
err = lfs2_file_write(&lfs2, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
for (lfs_size_t i = 0;
i < (cfg.block_count-26)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
err => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs2_remove(&lfs2, "exhaustion") => 0;
lfs2_remove(&lfs2, "exhaustiondir") => 0;
for (int i = 0; i < 10; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_remove(&lfs2, (char*)buffer) => 0;
}
// see that chained dir fails
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_sync(&lfs2, &file[0]) => 0;
for (int i = 0; i < 10; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
}
lfs2_mkdir(&lfs2, "exhaustiondir") => LFS2_ERR_NOSPC;
// shorten file to try a second chained dir
while (true) {
err = lfs2_mkdir(&lfs2, "exhaustiondir");
if (err != LFS2_ERR_NOSPC) {
break;
}
lfs2_ssize_t filesize = lfs2_file_size(&lfs2, &file[0]);
filesize > 0 => true;
lfs2_file_truncate(&lfs2, &file[0], filesize - size) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
}
err => 0;
lfs2_mkdir(&lfs2, "exhaustiondir2") => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
TEST
echo "--- Split dir test ---"
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
// create one block hole for half a directory
lfs2_file_open(&lfs2, &file[0], "bump", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs2_file_write(&lfs2, &file[0], buffer, cfg.block_size) => cfg.block_size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion", LFS2_O_WRONLY | LFS2_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < (cfg.block_count-4)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
// open hole
lfs2_remove(&lfs2, "bump") => 0;
lfs2_mkdir(&lfs2, "splitdir") => 0;
lfs2_file_open(&lfs2, &file[0], "splitdir/bump",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (lfs2_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs2_file_write(&lfs2, &file[0], buffer, 2*cfg.block_size) => LFS2_ERR_NOSPC;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Outdated lookahead test ---"
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
// fill completely with two files
lfs2_file_open(&lfs2, &file[0], "exhaustion1",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion2",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
// rewrite one file
lfs2_file_open(&lfs2, &file[0], "exhaustion1",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
// rewrite second file, this requires lookahead does not
// use old population
lfs2_file_open(&lfs2, &file[0], "exhaustion2",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
TEST
echo "--- Outdated lookahead and split dir test ---"
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
// fill completely with two files
lfs2_file_open(&lfs2, &file[0], "exhaustion1",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "exhaustion2",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
// remount to force reset of lookahead
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
// rewrite one file with a hole of one block
lfs2_file_open(&lfs2, &file[0], "exhaustion1",
LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs2_size_t i = 0;
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8);
i += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
// try to allocate a directory, should fail!
lfs2_mkdir(&lfs2, "split") => LFS2_ERR_NOSPC;
// file should not fail
lfs2_file_open(&lfs2, &file[0], "notasplit",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file[0], "hi", 2) => 2;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -1,286 +0,0 @@
#!/bin/bash
set -eu
echo "=== Attr tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs2_file_open(&lfs2, &file[0], "hello/hello",
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_write(&lfs2, &file[0], "hello", strlen("hello"))
=> strlen("hello");
lfs2_file_close(&lfs2, &file[0]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Set/get attribute ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_setattr(&lfs2, "hello", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "hello", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_removeattr(&lfs2, "hello", 'B') => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "hello", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "hello", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+10, 5) => 5;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_getattr(&lfs2, "hello", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "hello", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file[0]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Set/get root attribute ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_setattr(&lfs2, "/", 'A', "aaaa", 4) => 0;
lfs2_setattr(&lfs2, "/", 'B', "bbbbbb", 6) => 0;
lfs2_setattr(&lfs2, "/", 'C', "ccccc", 5) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "", 0) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 0;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_removeattr(&lfs2, "/", 'B') => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => LFS2_ERR_NOATTR;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "dddddd", 6) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 6;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'B', "eee", 3) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 3;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs2_setattr(&lfs2, "/", 'A', buffer, LFS2_ATTR_MAX+1) => LFS2_ERR_NOSPC;
lfs2_setattr(&lfs2, "/", 'B', "fffffffff", 9) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 6) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+10, 5) => 5;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_getattr(&lfs2, "/", 'A', buffer, 4) => 4;
lfs2_getattr(&lfs2, "/", 'B', buffer+4, 9) => 9;
lfs2_getattr(&lfs2, "/", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file[0]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Set/get file attribute ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
struct lfs2_attr attrs1[] = {
{'A', buffer, 4},
{'B', buffer+4, 6},
{'C', buffer+10, 5},
};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs2_file_close(&lfs2, &file[0]) => 0;
memset(buffer, 0, 15);
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 0;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "\0\0\0\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs2_file_close(&lfs2, &file[0]) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[1].size = 3;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs2_file_close(&lfs2, &file[0]) => 0;
memset(buffer, 0, 15);
attrs1[1].size = 6;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
attrs1[0].size = LFS2_ATTR_MAX+1;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1)
=> LFS2_ERR_NOSPC;
struct lfs2_attr attrs2[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs2_file_close(&lfs2, &file[0]) => 0;
attrs1[0].size = 4;
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg1) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
struct lfs2_attr attrs2[] = {
{'A', buffer, 4},
{'B', buffer+4, 9},
{'C', buffer+13, 5},
};
struct lfs2_file_config cfg2 = {.attrs=attrs2, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY, &cfg2) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs2_file_close(&lfs2, &file[0]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Deferred file attributes ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
struct lfs2_attr attrs1[] = {
{'B', "gggg", 4},
{'C', "", 0},
{'D', "hhhh", 4},
};
struct lfs2_file_config cfg1 = {.attrs=attrs1, .attr_count=3};
lfs2_file_opencfg(&lfs2, &file[0], "hello/hello", LFS2_O_WRONLY, &cfg1) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 9;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 5;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => LFS2_ERR_NOATTR;
memcmp(buffer, "fffffffff", 9) => 0;
memcmp(buffer+9, "ccccc\0\0\0\0", 9) => 0;
memcmp(buffer+18, "\0\0\0\0\0\0\0\0\0", 9) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'B', buffer, 9) => 4;
lfs2_getattr(&lfs2, "hello/hello", 'C', buffer+9, 9) => 0;
lfs2_getattr(&lfs2, "hello/hello", 'D', buffer+18, 9) => 4;
memcmp(buffer, "gggg\0\0\0\0\0", 9) => 0;
memcmp(buffer+9, "\0\0\0\0\0\0\0\0\0", 9) => 0;
memcmp(buffer+18, "hhhh\0\0\0\0\0", 9) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -6,113 +6,112 @@ echo "=== Corrupt tests ==="
NAMEMULT=64
FILEMULT=1
lfs2_mktree() {
lfs_mktree() {
tests/test.py ${1:-} << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < $NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[$NAMEMULT] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
lfs_mkdir(&lfs, (char*)buffer) => 0;
buffer[$NAMEMULT] = '/';
for (int j = 0; j < $NAMEMULT; j++) {
buffer[j+$NAMEMULT+1] = '0'+i;
}
buffer[2*$NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = $NAMEMULT;
for (int j = 0; j < i*$FILEMULT; j++) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
lfs2_chktree() {
lfs_chktree() {
tests/test.py ${1:-} << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (int i = 1; i < 10; i++) {
for (int j = 0; j < $NAMEMULT; j++) {
buffer[j] = '0'+i;
}
buffer[$NAMEMULT] = '\0';
lfs2_stat(&lfs2, (char*)buffer, &info) => 0;
info.type => LFS2_TYPE_DIR;
lfs_stat(&lfs, (char*)buffer, &info) => 0;
info.type => LFS_TYPE_DIR;
buffer[$NAMEMULT] = '/';
for (int j = 0; j < $NAMEMULT; j++) {
buffer[j+$NAMEMULT+1] = '0'+i;
}
buffer[2*$NAMEMULT+1] = '\0';
lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
size = $NAMEMULT;
for (int j = 0; j < i*$FILEMULT; j++) {
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(buffer, rbuffer, size) => 0;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
echo "--- Sanity check ---"
rm -rf blocks
lfs2_mktree
lfs2_chktree
BLOCKS="$(ls blocks | grep -vw '[01]')"
lfs_mktree
lfs_chktree
echo "--- Block corruption ---"
for b in $BLOCKS
for i in {0..33}
do
rm -rf blocks
mkdir blocks
ln -s /dev/zero blocks/$b
lfs2_mktree
lfs2_chktree
ln -s /dev/zero blocks/$(printf '%x' $i)
lfs_mktree
lfs_chktree
done
echo "--- Block persistance ---"
for b in $BLOCKS
for i in {0..33}
do
rm -rf blocks
mkdir blocks
lfs2_mktree
chmod a-w blocks/$b || true
lfs2_mktree
lfs2_chktree
lfs_mktree
chmod a-w blocks/$(printf '%x' $i)
lfs_mktree
lfs_chktree
done
echo "--- Big region corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..512}
for i in {2..255}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done
lfs2_mktree
lfs2_chktree
lfs_mktree
lfs_chktree
echo "--- Alternating corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..1024..2}
for i in {2..511..2}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done
lfs2_mktree
lfs2_chktree
lfs_mktree
lfs_chktree
echo "--- Results ---"
tests/stats.py

View File

@@ -6,294 +6,293 @@ LARGESIZE=128
echo "=== Directory tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Root directory ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Directory creation ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "potato") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- File creation ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "burito", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Directory iteration ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "potato") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Directory failures ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "potato") => LFS2_ERR_EXIST;
lfs2_dir_open(&lfs2, &dir[0], "tomato") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "burito") => LFS2_ERR_NOTDIR;
lfs2_file_open(&lfs2, &file[0], "tomato", LFS2_O_RDONLY) => LFS2_ERR_NOENT;
lfs2_file_open(&lfs2, &file[0], "potato", LFS2_O_RDONLY) => LFS2_ERR_ISDIR;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERR_NOTDIR;
lfs_file_open(&lfs, &file[0], "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file[0], "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Nested directories ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "potato/baked") => 0;
lfs2_mkdir(&lfs2, "potato/sweet") => 0;
lfs2_mkdir(&lfs2, "potato/fried") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "potato/baked") => 0;
lfs_mkdir(&lfs, "potato/sweet") => 0;
lfs_mkdir(&lfs, "potato/fried") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "potato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "potato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "baked") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Multi-block directory ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "cactus") => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "cactus") => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%03d", i);
lfs2_mkdir(&lfs2, (char*)buffer) => 0;
sprintf((char*)buffer, "cactus/test%d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "cactus") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
info.type => LFS_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "test%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
sprintf((char*)buffer, "test%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS2_TYPE_DIR;
}
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Directory remove ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "potato") => LFS2_ERR_NOTEMPTY;
lfs2_remove(&lfs2, "potato/sweet") => 0;
lfs2_remove(&lfs2, "potato/baked") => 0;
lfs2_remove(&lfs2, "potato/fried") => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "potato") => LFS_ERR_NOTEMPTY;
lfs_remove(&lfs, "potato/sweet") => 0;
lfs_remove(&lfs, "potato/baked") => 0;
lfs_remove(&lfs, "potato/fried") => 0;
lfs2_dir_open(&lfs2, &dir[0], "potato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_open(&lfs, &dir[0], "potato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs2_remove(&lfs2, "potato") => 0;
lfs_remove(&lfs, "potato") => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "cactus") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "cactus") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Directory rename ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "coldpotato") => 0;
lfs2_mkdir(&lfs2, "coldpotato/baked") => 0;
lfs2_mkdir(&lfs2, "coldpotato/sweet") => 0;
lfs2_mkdir(&lfs2, "coldpotato/fried") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_mkdir(&lfs, "coldpotato/baked") => 0;
lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
lfs_mkdir(&lfs, "coldpotato/fried") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "coldpotato", "hotpotato") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "hotpotato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "baked") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "warmpotato") => 0;
lfs2_mkdir(&lfs2, "warmpotato/mushy") => 0;
lfs2_rename(&lfs2, "hotpotato", "warmpotato") => LFS2_ERR_NOTEMPTY;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "warmpotato") => 0;
lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERR_INVAL;
lfs2_remove(&lfs2, "warmpotato/mushy") => 0;
lfs2_rename(&lfs2, "hotpotato", "warmpotato") => 0;
lfs_remove(&lfs, "warmpotato/mushy") => 0;
lfs_rename(&lfs, "hotpotato", "warmpotato") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "warmpotato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "baked") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "coldpotato") => 0;
lfs2_rename(&lfs2, "warmpotato/baked", "coldpotato/baked") => 0;
lfs2_rename(&lfs2, "warmpotato/sweet", "coldpotato/sweet") => 0;
lfs2_rename(&lfs2, "warmpotato/fried", "coldpotato/fried") => 0;
lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY;
lfs2_remove(&lfs2, "warmpotato") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "coldpotato") => 0;
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0;
lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
lfs_remove(&lfs, "warmpotato") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "coldpotato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "baked") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "sweet") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "fried") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Recursive remove ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "coldpotato") => LFS2_ERR_NOTEMPTY;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "coldpotato") => LFS_ERR_NOTEMPTY;
lfs2_dir_open(&lfs2, &dir[0], "coldpotato") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
while (true) {
int err = lfs2_dir_read(&lfs2, &dir[0], &info);
int err = lfs_dir_read(&lfs, &dir[0], &info);
err >= 0 => 1;
if (err == 0) {
break;
@@ -301,183 +300,59 @@ tests/test.py << TEST
strcpy((char*)buffer, "coldpotato/");
strcat((char*)buffer, info.name);
lfs2_remove(&lfs2, (char*)buffer) => 0;
lfs_remove(&lfs, (char*)buffer) => 0;
}
lfs2_remove(&lfs2, "coldpotato") => 0;
lfs_remove(&lfs, "coldpotato") => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "cactus") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Multi-block rename ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%03d", i);
sprintf((char*)wbuffer, "cactus/tedd%03d", i);
lfs2_rename(&lfs2, (char*)buffer, (char*)wbuffer) => 0;
}
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "cactus") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "tedd%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS2_TYPE_DIR;
}
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Multi-block remove ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "cactus") => LFS2_ERR_NOTEMPTY;
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/tedd%03d", i);
lfs2_remove(&lfs2, (char*)buffer) => 0;
sprintf((char*)buffer, "cactus/test%d", i);
lfs_remove(&lfs, (char*)buffer) => 0;
}
lfs2_remove(&lfs2, "cactus") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_remove(&lfs, "cactus") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Multi-block directory with files ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "prickly-pear") => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%03d", i);
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = 6;
memcpy(wbuffer, "Hello", size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "prickly-pear") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "test%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS2_TYPE_REG;
info.size => 6;
}
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Multi-block rename with files ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%03d", i);
sprintf((char*)wbuffer, "prickly-pear/tedd%03d", i);
lfs2_rename(&lfs2, (char*)buffer, (char*)wbuffer) => 0;
}
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "prickly-pear") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "tedd%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS2_TYPE_REG;
info.size => 6;
}
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Multi-block remove with files ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "prickly-pear") => LFS2_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/tedd%03d", i);
lfs2_remove(&lfs2, (char*)buffer) => 0;
}
lfs2_remove(&lfs2, "prickly-pear") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "burito") => 0;
info.type => LFS2_TYPE_REG;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
info.type => LFS_TYPE_REG;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

View File

@@ -1,221 +0,0 @@
#!/bin/bash
set -eu
# Note: These tests are intended for 512 byte inline size at different
# inline sizes they should still pass, but won't be testing anything
echo "=== Entry tests ==="
rm -rf blocks
function read_file {
cat << TEST
size = $2;
lfs2_file_open(&lfs2, &file[0], "$1", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
TEST
}
function write_file {
cat << TEST
size = $2;
lfs2_file_open(&lfs2, &file[0], "$1",
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
TEST
}
echo "--- Entry grow test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 20)
$(write_file "hi1" 20)
$(write_file "hi2" 20)
$(write_file "hi3" 20)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 20)
$(read_file "hi1" 200)
$(read_file "hi2" 20)
$(read_file "hi3" 20)
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Entry shrink test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 20)
$(write_file "hi1" 200)
$(write_file "hi2" 20)
$(write_file "hi3" 20)
$(read_file "hi1" 200)
$(write_file "hi1" 20)
$(read_file "hi0" 20)
$(read_file "hi1" 20)
$(read_file "hi2" 20)
$(read_file "hi3" 20)
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Entry spill test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 200)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Entry push spill test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 20)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Entry push spill two test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 20)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
$(write_file "hi4" 200)
$(read_file "hi1" 20)
$(write_file "hi1" 200)
$(read_file "hi0" 200)
$(read_file "hi1" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
$(read_file "hi4" 200)
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Entry drop test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 200)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
lfs2_remove(&lfs2, "hi1") => 0;
lfs2_stat(&lfs2, "hi1", &info) => LFS2_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs2_remove(&lfs2, "hi2") => 0;
lfs2_stat(&lfs2, "hi2", &info) => LFS2_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi3" 200)
lfs2_remove(&lfs2, "hi3") => 0;
lfs2_stat(&lfs2, "hi3", &info) => LFS2_ERR_NOENT;
$(read_file "hi0" 200)
lfs2_remove(&lfs2, "hi0") => 0;
lfs2_stat(&lfs2, "hi0", &info) => LFS2_ERR_NOENT;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Create too big ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 400;
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
size = 400;
lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Resize too big ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 40;
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
size = 40;
lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
size = 400;
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
size = 400;
lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -8,65 +8,65 @@ LARGESIZE=262144
echo "=== File tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Simple file test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("Hello World!\n");
memcpy(wbuffer, "Hello World!\n", size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "hello", LFS2_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDONLY) => 0;
size = strlen("Hello World!\n");
lfs2_file_read(&lfs2, &file[0], rbuffer, size) => size;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
w_test() {
tests/test.py ${4:-} << TEST
size = $1;
lfs2_size_t chunk = 31;
tests/test.py << TEST
lfs_size_t size = $1;
lfs_size_t chunk = 31;
srand(0);
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "$2",
${3:-LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC}) => 0;
for (lfs2_size_t i = 0; i < size; i += chunk) {
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "$2",
${3:-LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC}) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i;
for (lfs2_size_t b = 0; b < chunk; b++) {
for (lfs_size_t b = 0; b < chunk; b++) {
buffer[b] = rand() & 0xff;
}
lfs2_file_write(&lfs2, &file[0], buffer, chunk) => chunk;
lfs_file_write(&lfs, &file[0], buffer, chunk) => chunk;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
r_test() {
tests/test.py << TEST
size = $1;
lfs2_size_t chunk = 29;
lfs_size_t size = $1;
lfs_size_t chunk = 29;
srand(0);
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "$2", &info) => 0;
info.type => LFS2_TYPE_REG;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "$2", &info) => 0;
info.type => LFS_TYPE_REG;
info.size => size;
lfs2_file_open(&lfs2, &file[0], "$2", ${3:-LFS2_O_RDONLY}) => 0;
for (lfs2_size_t i = 0; i < size; i += chunk) {
lfs_file_open(&lfs, &file[0], "$2", ${3:-LFS_O_RDONLY}) => 0;
for (lfs_size_t i = 0; i < size; i += chunk) {
chunk = (chunk < size - i) ? chunk : size - i;
lfs2_file_read(&lfs2, &file[0], buffer, chunk) => chunk;
for (lfs2_size_t b = 0; b < chunk && i+b < size; b++) {
lfs_file_read(&lfs, &file[0], buffer, chunk) => chunk;
for (lfs_size_t b = 0; b < chunk && i+b < size; b++) {
buffer[b] => rand() & 0xff;
}
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
@@ -106,52 +106,33 @@ r_test 0 noavacado
echo "--- Dir check ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
info.type => LFS2_TYPE_REG;
info.type => LFS_TYPE_REG;
info.size => strlen("Hello World!\n");
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "largeavacado") => 0;
info.type => LFS2_TYPE_REG;
info.size => $LARGESIZE;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "mediumavacado") => 0;
info.type => LFS2_TYPE_REG;
info.size => $MEDIUMSIZE;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "noavacado") => 0;
info.type => LFS2_TYPE_REG;
info.size => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "smallavacado") => 0;
info.type => LFS2_TYPE_REG;
info.type => LFS_TYPE_REG;
info.size => $SMALLSIZE;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Many file test ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
TEST
tests/test.py << TEST
// Create 300 files of 6 bytes
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "directory") => 0;
for (unsigned i = 0; i < 300; i++) {
snprintf((char*)buffer, sizeof(buffer), "file_%03d", i);
lfs2_file_open(&lfs2, &file[0], (char*)buffer, LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
size = 6;
memcpy(wbuffer, "Hello", size);
lfs2_file_write(&lfs2, &file[0], wbuffer, size) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "mediumavacado") => 0;
info.type => LFS_TYPE_REG;
info.size => $MEDIUMSIZE;
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;
info.type => LFS_TYPE_REG;
info.size => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

View File

@@ -6,44 +6,43 @@ rm -rf blocks
echo "--- Basic formatting ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
TEST
echo "--- Basic mounting ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => LFS2_ERR_NOSPC;
lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
rm blocks/0 blocks/1
echo "--- Invalid mount ---"
echo "--- Basic mounting ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => LFS2_ERR_CORRUPT;
lfs_format(&lfs, &cfg) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Expanding superblock ---"
echo "--- Invalid mount ---"
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
for (int i = 0; i < 100; i++) {
lfs2_mkdir(&lfs2, "dummy") => 0;
lfs2_remove(&lfs2, "dummy") => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
rm blocks/0 blocks/1
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "dummy") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
echo "--- Valid corrupt mount ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
TEST
rm blocks/0
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

View File

@@ -1,186 +0,0 @@
#!/bin/bash
set -eu
echo "=== Interspersed tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
TEST
echo "--- Interspersed file test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "a", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &file[1], "b", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &file[2], "c", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &file[3], "d", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int i = 0; i < 10; i++) {
lfs2_file_write(&lfs2, &file[0], (const void*)"a", 1) => 1;
lfs2_file_write(&lfs2, &file[1], (const void*)"b", 1) => 1;
lfs2_file_write(&lfs2, &file[2], (const void*)"c", 1) => 1;
lfs2_file_write(&lfs2, &file[3], (const void*)"d", 1) => 1;
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_file_close(&lfs2, &file[1]);
lfs2_file_close(&lfs2, &file[2]);
lfs2_file_close(&lfs2, &file[3]);
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "a") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "b") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "c") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "d") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "a", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file[1], "b", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file[2], "c", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file[3], "d", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1;
buffer[0] => 'a';
lfs2_file_read(&lfs2, &file[1], buffer, 1) => 1;
buffer[0] => 'b';
lfs2_file_read(&lfs2, &file[2], buffer, 1) => 1;
buffer[0] => 'c';
lfs2_file_read(&lfs2, &file[3], buffer, 1) => 1;
buffer[0] => 'd';
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_file_close(&lfs2, &file[1]);
lfs2_file_close(&lfs2, &file[2]);
lfs2_file_close(&lfs2, &file[3]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Interspersed remove file test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int i = 0; i < 5; i++) {
lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1;
}
lfs2_remove(&lfs2, "a") => 0;
lfs2_remove(&lfs2, "b") => 0;
lfs2_remove(&lfs2, "c") => 0;
lfs2_remove(&lfs2, "d") => 0;
for (int i = 0; i < 5; i++) {
lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1;
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "e") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1;
buffer[0] => 'e';
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Remove inconveniently test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_WRONLY | LFS2_O_TRUNC) => 0;
lfs2_file_open(&lfs2, &file[1], "f", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
lfs2_file_open(&lfs2, &file[2], "g", LFS2_O_WRONLY | LFS2_O_CREAT) => 0;
for (int i = 0; i < 5; i++) {
lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &file[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &file[2], (const void*)"g", 1) => 1;
}
lfs2_remove(&lfs2, "f") => 0;
for (int i = 0; i < 5; i++) {
lfs2_file_write(&lfs2, &file[0], (const void*)"e", 1) => 1;
lfs2_file_write(&lfs2, &file[1], (const void*)"f", 1) => 1;
lfs2_file_write(&lfs2, &file[2], (const void*)"g", 1) => 1;
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_file_close(&lfs2, &file[1]);
lfs2_file_close(&lfs2, &file[2]);
lfs2_dir_open(&lfs2, &dir[0], "/") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS2_TYPE_DIR;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "e") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "g") => 0;
info.type => LFS2_TYPE_REG;
info.size => 10;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_file_open(&lfs2, &file[0], "e", LFS2_O_RDONLY) => 0;
lfs2_file_open(&lfs2, &file[1], "g", LFS2_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, 1) => 1;
buffer[0] => 'e';
lfs2_file_read(&lfs2, &file[1], buffer, 1) => 1;
buffer[0] => 'g';
}
lfs2_file_close(&lfs2, &file[0]);
lfs2_file_close(&lfs2, &file[1]);
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -4,327 +4,231 @@ set -eu
echo "=== Move tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "a") => 0;
lfs2_mkdir(&lfs2, "b") => 0;
lfs2_mkdir(&lfs2, "c") => 0;
lfs2_mkdir(&lfs2, "d") => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "a") => 0;
lfs_mkdir(&lfs, "b") => 0;
lfs_mkdir(&lfs, "c") => 0;
lfs_mkdir(&lfs, "d") => 0;
lfs2_mkdir(&lfs2, "a/hi") => 0;
lfs2_mkdir(&lfs2, "a/hi/hola") => 0;
lfs2_mkdir(&lfs2, "a/hi/bonjour") => 0;
lfs2_mkdir(&lfs2, "a/hi/ohayo") => 0;
lfs_mkdir(&lfs, "a/hi") => 0;
lfs_mkdir(&lfs, "a/hi/hola") => 0;
lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
lfs2_file_open(&lfs2, &file[0], "a/hello", LFS2_O_CREAT | LFS2_O_WRONLY) => 0;
lfs2_file_write(&lfs2, &file[0], "hola\n", 5) => 5;
lfs2_file_write(&lfs2, &file[0], "bonjour\n", 8) => 8;
lfs2_file_write(&lfs2, &file[0], "ohayo\n", 6) => 6;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_open(&lfs, &file[0], "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
lfs_file_write(&lfs, &file[0], "hola\n", 5) => 5;
lfs_file_write(&lfs, &file[0], "bonjour\n", 8) => 8;
lfs_file_write(&lfs, &file[0], "ohayo\n", 6) => 6;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move file ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "a/hello", "b/hello") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "a/hello", "b/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "a") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "b") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move file corrupt source ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "b/hello", "c/hello") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "b/hello", "c/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 1
rm -v blocks/7
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "b") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move file corrupt source and dest ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "c/hello", "d/hello") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 2
rm -v blocks/8
rm -v blocks/a
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "d") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "d") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Move file after corrupt ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "c/hello", "d/hello") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "d") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "a/hi", "b/hi") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "a/hi", "b/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "a") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "b") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir corrupt source ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "b/hi", "c/hi") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "b/hi", "c/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 1
rm -v blocks/7
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "b") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir corrupt source and dest ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "c/hi", "d/hi") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 2
rm -v blocks/9
rm -v blocks/a
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "d") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Move dir after corrupt ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_rename(&lfs2, "c/hi", "d/hi") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "c") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "d") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "d") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move check ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a/hi") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "b/hi") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "c/hi") => LFS2_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "d/hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_open(&lfs, &dir[0], "c/hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a/hello") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "b/hello") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "c/hello") => LFS2_ERR_NOENT;
lfs2_file_open(&lfs2, &file[0], "d/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 5) => 5;
memcmp(buffer, "hola\n", 5) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 8) => 8;
memcmp(buffer, "bonjour\n", 8) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 6) => 6;
memcmp(buffer, "ohayo\n", 6) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Move state stealing ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_remove(&lfs2, "b") => 0;
lfs2_remove(&lfs2, "c") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a/hi") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "b") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "c") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "d/hi") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
strcmp(info.name, "hola") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs2_dir_open(&lfs2, &dir[0], "a/hello") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "b") => LFS2_ERR_NOENT;
lfs2_dir_open(&lfs2, &dir[0], "c") => LFS2_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT;
lfs2_file_open(&lfs2, &file[0], "d/hello", LFS2_O_RDONLY) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 5) => 5;
lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, 5) => 5;
memcmp(buffer, "hola\n", 5) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 8) => 8;
lfs_file_read(&lfs, &file[0], buffer, 8) => 8;
memcmp(buffer, "bonjour\n", 8) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, 6) => 6;
lfs_file_read(&lfs, &file[0], buffer, 6) => 6;
memcmp(buffer, "ohayo\n", 6) => 0;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -4,41 +4,37 @@ set -eu
echo "=== Orphan tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Orphan test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "parent") => 0;
lfs2_mkdir(&lfs2, "parent/orphan") => 0;
lfs2_mkdir(&lfs2, "parent/child") => 0;
lfs2_remove(&lfs2, "parent/orphan") => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "parent") => 0;
lfs_mkdir(&lfs, "parent/orphan") => 0;
lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0;
TEST
# corrupt most recent commit, this should be the update to the previous
# remove most recent file, this should be the update to the previous
# linked-list entry and should orphan the child
tests/corrupt.py
rm -v blocks/8
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
unsigned before = 0;
lfs_traverse(&lfs, test_count, &before) => 0;
test_log("before", before);
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_ssize_t before = lfs2_fs_size(&lfs2);
before => 8;
lfs_deorphan(&lfs) => 0;
lfs2_unmount(&lfs2) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
unsigned after = 0;
lfs_traverse(&lfs, test_count, &after) => 0;
test_log("after", after);
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_ssize_t orphaned = lfs2_fs_size(&lfs2);
orphaned => 8;
lfs2_mkdir(&lfs2, "parent/otherchild") => 0;
lfs2_stat(&lfs2, "parent/orphan", &info) => LFS2_ERR_NOENT;
lfs2_ssize_t deorphaned = lfs2_fs_size(&lfs2);
deorphaned => 8;
lfs2_unmount(&lfs2) => 0;
int diff = before - after;
diff => 2;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

186
tests/test_parallel.sh Executable file
View File

@@ -0,0 +1,186 @@
#!/bin/bash
set -eu
echo "=== Parallel tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
TEST
echo "--- Parallel file test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &file[1], "b", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &file[2], "c", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &file[3], "d", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int i = 0; i < 10; i++) {
lfs_file_write(&lfs, &file[0], (const void*)"a", 1) => 1;
lfs_file_write(&lfs, &file[1], (const void*)"b", 1) => 1;
lfs_file_write(&lfs, &file[2], (const void*)"c", 1) => 1;
lfs_file_write(&lfs, &file[3], (const void*)"d", 1) => 1;
}
lfs_file_close(&lfs, &file[0]);
lfs_file_close(&lfs, &file[1]);
lfs_file_close(&lfs, &file[2]);
lfs_file_close(&lfs, &file[3]);
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "a") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "b") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "c") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "d") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_file_open(&lfs, &file[0], "a", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[1], "b", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[2], "c", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[3], "d", LFS_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
buffer[0] => 'a';
lfs_file_read(&lfs, &file[1], buffer, 1) => 1;
buffer[0] => 'b';
lfs_file_read(&lfs, &file[2], buffer, 1) => 1;
buffer[0] => 'c';
lfs_file_read(&lfs, &file[3], buffer, 1) => 1;
buffer[0] => 'd';
}
lfs_file_close(&lfs, &file[0]);
lfs_file_close(&lfs, &file[1]);
lfs_file_close(&lfs, &file[2]);
lfs_file_close(&lfs, &file[3]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Parallel remove file test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
}
lfs_remove(&lfs, "a") => 0;
lfs_remove(&lfs, "b") => 0;
lfs_remove(&lfs, "c") => 0;
lfs_remove(&lfs, "d") => 0;
for (int i = 0; i < 5; i++) {
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
}
lfs_file_close(&lfs, &file[0]);
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "e") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
buffer[0] => 'e';
}
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Remove inconveniently test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_open(&lfs, &file[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_open(&lfs, &file[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (int i = 0; i < 5; i++) {
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1;
}
lfs_remove(&lfs, "f") => 0;
for (int i = 0; i < 5; i++) {
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1;
lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1;
}
lfs_file_close(&lfs, &file[0]);
lfs_file_close(&lfs, &file[1]);
lfs_file_close(&lfs, &file[2]);
lfs_dir_open(&lfs, &dir[0], "/") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "e") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "g") => 0;
info.type => LFS_TYPE_REG;
info.size => 10;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0;
lfs_file_open(&lfs, &file[1], "g", LFS_O_RDONLY) => 0;
for (int i = 0; i < 10; i++) {
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
buffer[0] => 'e';
lfs_file_read(&lfs, &file[1], buffer, 1) => 1;
buffer[0] => 'g';
}
lfs_file_close(&lfs, &file[0]);
lfs_file_close(&lfs, &file[1]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -4,165 +4,119 @@ set -eu
echo "=== Path tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "tea") => 0;
lfs2_mkdir(&lfs2, "coffee") => 0;
lfs2_mkdir(&lfs2, "soda") => 0;
lfs2_mkdir(&lfs2, "tea/hottea") => 0;
lfs2_mkdir(&lfs2, "tea/warmtea") => 0;
lfs2_mkdir(&lfs2, "tea/coldtea") => 0;
lfs2_mkdir(&lfs2, "coffee/hotcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/warmcoffee") => 0;
lfs2_mkdir(&lfs2, "coffee/coldcoffee") => 0;
lfs2_mkdir(&lfs2, "soda/hotsoda") => 0;
lfs2_mkdir(&lfs2, "soda/warmsoda") => 0;
lfs2_mkdir(&lfs2, "soda/coldsoda") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "tea") => 0;
lfs_mkdir(&lfs, "coffee") => 0;
lfs_mkdir(&lfs, "soda") => 0;
lfs_mkdir(&lfs, "tea/hottea") => 0;
lfs_mkdir(&lfs, "tea/warmtea") => 0;
lfs_mkdir(&lfs, "tea/coldtea") => 0;
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
lfs_mkdir(&lfs, "soda/hotsoda") => 0;
lfs_mkdir(&lfs, "soda/warmsoda") => 0;
lfs_mkdir(&lfs, "soda/coldsoda") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Root path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "tea/hottea", &info) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "/tea/hottea", &info) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "/milk1") => 0;
lfs2_stat(&lfs2, "/milk1", &info) => 0;
lfs_mkdir(&lfs, "/milk1") => 0;
lfs_stat(&lfs, "/milk1", &info) => 0;
strcmp(info.name, "milk1") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Redundant slash path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "/tea/hottea", &info) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "//tea//hottea", &info) => 0;
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "///tea///hottea", &info) => 0;
lfs_stat(&lfs, "///tea///hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "///milk2") => 0;
lfs2_stat(&lfs2, "///milk2", &info) => 0;
lfs_mkdir(&lfs, "///milk2") => 0;
lfs_stat(&lfs, "///milk2", &info) => 0;
strcmp(info.name, "milk2") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dot path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "./tea/hottea", &info) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "/./tea/hottea", &info) => 0;
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "/././tea/hottea", &info) => 0;
lfs_stat(&lfs, "/././tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "/./tea/./hottea", &info) => 0;
lfs_stat(&lfs, "/./tea/./hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "/./milk3") => 0;
lfs2_stat(&lfs2, "/./milk3", &info) => 0;
lfs_mkdir(&lfs, "/./milk3") => 0;
lfs_stat(&lfs, "/./milk3", &info) => 0;
strcmp(info.name, "milk3") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dot dot path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "coffee/../tea/hottea", &info) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "tea/coldtea/../hottea", &info) => 0;
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "coffee/../soda/../tea/hottea", &info) => 0;
lfs_stat(&lfs, "coffee/../soda/../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "coffee/../milk4") => 0;
lfs2_stat(&lfs2, "coffee/../milk4", &info) => 0;
lfs_mkdir(&lfs, "coffee/../milk4") => 0;
lfs_stat(&lfs, "coffee/../milk4", &info) => 0;
strcmp(info.name, "milk4") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Trailing dot path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "tea/hottea/", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "tea/hottea/.", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "tea/hottea/./.", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_stat(&lfs2, "tea/hottea/..", &info) => 0;
strcmp(info.name, "tea") => 0;
lfs2_stat(&lfs2, "tea/hottea/../.", &info) => 0;
strcmp(info.name, "tea") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Root dot dot path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "coffee/../../../../../../tea/hottea", &info) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
strcmp(info.name, "hottea") => 0;
lfs2_mkdir(&lfs2, "coffee/../../../../../../milk5") => 0;
lfs2_stat(&lfs2, "coffee/../../../../../../milk5", &info) => 0;
lfs_mkdir(&lfs, "coffee/../../../../../../milk5") => 0;
lfs_stat(&lfs, "coffee/../../../../../../milk5", &info) => 0;
strcmp(info.name, "milk5") => 0;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Root tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_stat(&lfs2, "/", &info) => 0;
info.type => LFS2_TYPE_DIR;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "/", &info) => 0;
info.type => LFS_TYPE_DIR;
strcmp(info.name, "/") => 0;
lfs2_mkdir(&lfs2, "/") => LFS2_ERR_EXIST;
lfs2_file_open(&lfs2, &file[0], "/", LFS2_O_WRONLY | LFS2_O_CREAT)
=> LFS2_ERR_ISDIR;
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Sketchy path tests ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "dirt/ground") => LFS2_ERR_NOENT;
lfs2_mkdir(&lfs2, "dirt/ground/earth") => LFS2_ERR_NOENT;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Superblock conflict test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "littlefs") => 0;
lfs2_remove(&lfs2, "littlefs") => 0;
lfs2_unmount(&lfs2) => 0;
TEST
echo "--- Max path test ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
memset(buffer, 'w', LFS2_NAME_MAX+1);
buffer[LFS2_NAME_MAX+2] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => LFS2_ERR_NAMETOOLONG;
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG;
memcpy(buffer, "coffee/", strlen("coffee/"));
memset(buffer+strlen("coffee/"), 'w', LFS2_NAME_MAX+1);
buffer[strlen("coffee/")+LFS2_NAME_MAX+2] = '\0';
lfs2_mkdir(&lfs2, (char*)buffer) => LFS2_ERR_NAMETOOLONG;
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT) => LFS2_ERR_NAMETOOLONG;
lfs2_unmount(&lfs2) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT;
lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

View File

@@ -8,353 +8,353 @@ LARGESIZE=132
echo "=== Seek tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_mkdir(&lfs2, "hello") => 0;
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "hello/kitty%03d", i);
lfs2_file_open(&lfs2, &file[0], (char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_APPEND) => 0;
sprintf((char*)buffer, "hello/kitty%d", i);
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("kittycatcat");
memcpy(buffer, "kittycatcat", size);
for (int j = 0; j < $LARGESIZE; j++) {
lfs2_file_write(&lfs2, &file[0], buffer, size);
lfs_file_write(&lfs, &file[0], buffer, size);
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Simple dir seek ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
int i;
for (i = 0; i < $SMALLSIZE; i++) {
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
pos = lfs2_dir_tell(&lfs2, &dir[0]);
pos = lfs_dir_tell(&lfs, &dir[0]);
}
pos >= 0 => 1;
lfs2_dir_seek(&lfs2, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_rewind(&lfs2, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%03d", 0);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_rewind(&lfs, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%d", 0);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_seek(&lfs2, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Large dir seek ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_dir_open(&lfs2, &dir[0], "hello") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
int i;
for (i = 0; i < $MEDIUMSIZE; i++) {
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
pos = lfs2_dir_tell(&lfs2, &dir[0]);
pos = lfs_dir_tell(&lfs, &dir[0]);
}
pos >= 0 => 1;
lfs2_dir_seek(&lfs2, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_rewind(&lfs2, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%03d", 0);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_rewind(&lfs, &dir[0]) => 0;
sprintf((char*)buffer, "kitty%d", 0);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_seek(&lfs2, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%03d", i);
lfs2_dir_read(&lfs2, &dir[0], &info) => 1;
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
sprintf((char*)buffer, "kitty%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
lfs2_dir_close(&lfs2, &dir[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Simple file seek ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDONLY) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
size = strlen("kittycatcat");
for (int i = 0; i < $SMALLSIZE; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs2_file_tell(&lfs2, &file[0]);
pos = lfs_file_tell(&lfs, &file[0]);
}
pos >= 0 => 1;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_rewind(&lfs2, &file[0]) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_rewind(&lfs, &file[0]) => 0;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], size, LFS2_SEEK_CUR) => 3*size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_CUR) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file[0]);
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Large file seek ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDONLY) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
size = strlen("kittycatcat");
for (int i = 0; i < $MEDIUMSIZE; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs2_file_tell(&lfs2, &file[0]);
pos = lfs_file_tell(&lfs, &file[0]);
}
pos >= 0 => 1;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_rewind(&lfs2, &file[0]) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_rewind(&lfs, &file[0]) => 0;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], size, LFS2_SEEK_CUR) => 3*size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], size, LFS_SEEK_CUR) => 3*size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_CUR) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file[0]);
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Simple file seek and write ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
size = strlen("kittycatcat");
for (int i = 0; i < $SMALLSIZE; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
pos = lfs2_file_tell(&lfs2, &file[0]);
pos = lfs_file_tell(&lfs, &file[0]);
}
pos >= 0 => 1;
memcpy(buffer, "doggodogdog", size);
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_rewind(&lfs2, &file[0]) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_rewind(&lfs, &file[0]) => 0;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file[0]);
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Large file seek and write ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
lfs2_soff_t pos;
lfs_soff_t pos;
size = strlen("kittycatcat");
for (int i = 0; i < $MEDIUMSIZE; i++) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
if (i != $SMALLSIZE) {
memcmp(buffer, "kittycatcat", size) => 0;
}
pos = lfs2_file_tell(&lfs2, &file[0]);
pos = lfs_file_tell(&lfs, &file[0]);
}
pos >= 0 => 1;
memcpy(buffer, "doggodogdog", size);
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_rewind(&lfs2, &file[0]) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_rewind(&lfs, &file[0]) => 0;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_seek(&lfs2, &file[0], pos, LFS2_SEEK_SET) => pos;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "doggodogdog", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -size, LFS2_SEEK_END) >= 0 => 1;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
size = lfs2_file_size(&lfs2, &file[0]);
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_CUR) => size;
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Boundary seek and write ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
size = strlen("hedgehoghog");
const lfs2_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs2_soff_t off = offsets[i];
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs_soff_t off = offsets[i];
memcpy(buffer, "hedgehoghog", size);
lfs2_file_seek(&lfs2, &file[0], off, LFS2_SEEK_SET) => off;
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs2_file_seek(&lfs2, &file[0], off, LFS2_SEEK_SET) => off;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hedgehoghog", size) => 0;
lfs2_file_seek(&lfs2, &file[0], 0, LFS2_SEEK_SET) => 0;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_SET) => 0;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "kittycatcat", size) => 0;
lfs2_file_sync(&lfs2, &file[0]) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Out-of-bounds seek ---"
tests/test.py << TEST
lfs2_mount(&lfs2, &cfg) => 0;
lfs2_file_open(&lfs2, &file[0], "hello/kitty042", LFS2_O_RDWR) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
size = strlen("kittycatcat");
lfs2_file_size(&lfs2, &file[0]) => $LARGESIZE*size;
lfs2_file_seek(&lfs2, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => 0;
lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size;
lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
memcpy(buffer, "porcupineee", size);
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
lfs_file_write(&lfs, &file[0], buffer, size) => size;
lfs2_file_seek(&lfs2, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
LFS2_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "porcupineee", size) => 0;
lfs2_file_seek(&lfs2, &file[0], $LARGESIZE*size,
LFS2_SEEK_SET) => $LARGESIZE*size;
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_seek(&lfs, &file[0], $LARGESIZE*size,
LFS_SEEK_SET) => $LARGESIZE*size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0;
lfs2_file_seek(&lfs2, &file[0], -(($LARGESIZE+$SMALLSIZE)*size),
LFS2_SEEK_CUR) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file[0]) => ($LARGESIZE+1)*size;
lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+$SMALLSIZE)*size),
LFS_SEEK_CUR) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size;
lfs2_file_seek(&lfs2, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size),
LFS2_SEEK_END) => LFS2_ERR_INVAL;
lfs2_file_tell(&lfs2, &file[0]) => ($LARGESIZE+1)*size;
lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size),
LFS_SEEK_END) => LFS_ERR_INVAL;
lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size;
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs2_unmount(&lfs2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"

View File

@@ -8,127 +8,117 @@ LARGESIZE=8192
echo "=== Truncate tests ==="
rm -rf blocks
tests/test.py << TEST
lfs2_format(&lfs2, &cfg) => 0;
lfs_format(&lfs, &cfg) => 0;
TEST
truncate_test() {
STARTSIZES="$1"
STARTSEEKS="$2"
HOTSIZES="$3"
COLDSIZES="$4"
HOTSIZES="$2"
COLDSIZES="$3"
tests/test.py << TEST
static const lfs2_off_t startsizes[] = {$STARTSIZES};
static const lfs2_off_t startseeks[] = {$STARTSEEKS};
static const lfs2_off_t hotsizes[] = {$HOTSIZES};
static const lfs_off_t startsizes[] = {$STARTSIZES};
static const lfs_off_t hotsizes[] = {$HOTSIZES};
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file[0], (const char*)buffer,
LFS2_O_WRONLY | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
lfs_file_open(&lfs, &file[0], (const char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
strcpy((char*)buffer, "hair");
size = strlen((char*)buffer);
for (lfs2_off_t j = 0; j < startsizes[i]; j += size) {
lfs2_file_write(&lfs2, &file[0], buffer, size) => size;
for (int j = 0; j < startsizes[i]; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs2_file_size(&lfs2, &file[0]) => startsizes[i];
lfs_file_size(&lfs, &file[0]) => startsizes[i];
if (startseeks[i] != startsizes[i]) {
lfs2_file_seek(&lfs2, &file[0],
startseeks[i], LFS2_SEEK_SET) => startseeks[i];
}
lfs_file_truncate(&lfs, &file[0], hotsizes[i]) => 0;
lfs_file_size(&lfs, &file[0]) => hotsizes[i];
lfs2_file_truncate(&lfs2, &file[0], hotsizes[i]) => 0;
lfs2_file_size(&lfs2, &file[0]) => hotsizes[i];
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
static const lfs2_off_t startsizes[] = {$STARTSIZES};
static const lfs2_off_t hotsizes[] = {$HOTSIZES};
static const lfs2_off_t coldsizes[] = {$COLDSIZES};
static const lfs_off_t startsizes[] = {$STARTSIZES};
static const lfs_off_t hotsizes[] = {$HOTSIZES};
static const lfs_off_t coldsizes[] = {$COLDSIZES};
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file[0], (const char*)buffer, LFS2_O_RDWR) => 0;
lfs2_file_size(&lfs2, &file[0]) => hotsizes[i];
lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file[0]) => hotsizes[i];
size = strlen("hair");
lfs2_off_t j = 0;
int j = 0;
for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < hotsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs2_file_truncate(&lfs2, &file[0], coldsizes[i]) => 0;
lfs2_file_size(&lfs2, &file[0]) => coldsizes[i];
lfs_file_truncate(&lfs, &file[0], coldsizes[i]) => 0;
lfs_file_size(&lfs, &file[0]) => coldsizes[i];
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
static const lfs2_off_t startsizes[] = {$STARTSIZES};
static const lfs2_off_t hotsizes[] = {$HOTSIZES};
static const lfs2_off_t coldsizes[] = {$COLDSIZES};
static const lfs_off_t startsizes[] = {$STARTSIZES};
static const lfs_off_t hotsizes[] = {$HOTSIZES};
static const lfs_off_t coldsizes[] = {$COLDSIZES};
lfs2_mount(&lfs2, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i);
lfs2_file_open(&lfs2, &file[0], (const char*)buffer, LFS2_O_RDONLY) => 0;
lfs2_file_size(&lfs2, &file[0]) => coldsizes[i];
lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file[0]) => coldsizes[i];
size = strlen("hair");
lfs2_off_t j = 0;
int j = 0;
for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
j += size) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0;
}
for (; j < coldsizes[i]; j += size) {
lfs2_file_read(&lfs2, &file[0], buffer, size) => size;
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "\0\0\0\0", size) => 0;
}
lfs2_file_close(&lfs2, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
}
lfs2_unmount(&lfs2) => 0;
lfs_unmount(&lfs) => 0;
TEST
}
echo "--- Cold shrinking truncate ---"
truncate_test \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE"
echo "--- Cold expanding truncate ---"
truncate_test \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"
echo "--- Warm shrinking truncate ---"
truncate_test \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, 0, 0, 0, 0"
@@ -136,21 +126,6 @@ truncate_test \
echo "--- Warm expanding truncate ---"
truncate_test \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"
echo "--- Mid-file shrinking truncate ---"
truncate_test \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
" $LARGESIZE, $LARGESIZE, $LARGESIZE, $LARGESIZE, $LARGESIZE" \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, 0, 0, 0, 0"
echo "--- Mid-file expanding truncate ---"
truncate_test \
" 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE, 2*$LARGESIZE" \
" 0, 0, $SMALLSIZE, $MEDIUMSIZE, $LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \
"2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE"