mirror of
https://gitee.com/openharmony/third_party_littlefs
synced 2024-11-27 09:01:27 +00:00
ce2c01f098
Sometimes small, single line code change hides behind it a complicated story. This is one of those times. If you look at this diff, you may note that this is a case of lfs_dir_fetchmatch not correctly handling a tag that invalidates a callback used to search for some condition, in this case a search for a parent, which is invalidated by a later dir tag overwritting the previous dir pair. But how can this happen? Dir-pair-tags are only overwritten during relocations (when a block goes bad or exceeds the block_cycles config option for dynamic wear-leveling). Other dir operations create new directory entries. And the only lfs_dir_fetchmatch condition that relies on overwrites (as opposed to proper deletes) is when we need to find a directory's parent, an operation that only occurs during a _different_ relocation. And a false _positive_, can only happen if we don't have a parent. Which is really unlikely when we search for directory parents! This bug and minimal test case was found by Matthew Renzelmann. In a unfortunate series of events, first a file creation causes a directory split to occur. This creates a new, orphaned metadata-pair containing our new file. However, the revision count on this metadata-pair indicates the pair is due for relocation as a part of wear-leveling. Normally, this is fine, even though this metadata-pair has no parent, the lfs_dir_find should return ENOENT and continue without error. However, here we get hit by our fetchmatch bug. A previous, unrelated relocation overwrites a pair which just happens to contain the block allocated for a new metadata-pair. When we search for a parent, lfs_dir_fetchmatch incorrectly finds this old, outdated metadata pair and incorrectly tells our orphan it's found its parent. As you can imagine the orphan's dissapointment must be immense. So an unfortunately timed dir split triggers a relocation which incorrectly finds a previously written parent that has been outdated by another relocation. As a solution we can outdate our found tag if it is overwritten by an exact match during lfs_dir_fetchmatch. As a part of this I started adding a new set of tests: tests/test_relocations, for aggressive relocations tests. This is already by appended to by another PR. I suspect relocations is relatively under-tested and is becoming more important due to recent improvements in wear-leveling.
319 lines
11 KiB
YAML
319 lines
11 KiB
YAML
# 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+="
|
|
-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"
|
|
|
|
# run tests
|
|
- make test QUIET=1
|
|
|
|
# run tests with a few different configurations
|
|
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_CACHE_SIZE=4"
|
|
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_CACHE_SIZE=512 -DLFS_BLOCK_CYCLES=16"
|
|
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=8 -DLFS_CACHE_SIZE=16 -DLFS_BLOCK_CYCLES=2"
|
|
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD_SIZE=256"
|
|
|
|
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
|
|
- make clean test QUIET=1 CFLAGS+="-DLFS_EMUBD_ERASE_VALUE=0xff"
|
|
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
|
|
|
|
# additional configurations that don't support all tests (this should be
|
|
# fixed but at the moment it is what it is)
|
|
- make test_files QUIET=1
|
|
CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_BLOCK_SIZE=4096"
|
|
- make test_files QUIET=1
|
|
CFLAGS+="-DLFS_READ_SIZE=\(2*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
|
|
- make test_files QUIET=1
|
|
CFLAGS+="-DLFS_READ_SIZE=\(8*1024\) -DLFS_BLOCK_SIZE=\(64*1024\)"
|
|
- make test_files QUIET=1
|
|
CFLAGS+="-DLFS_READ_SIZE=11 -DLFS_BLOCK_SIZE=704"
|
|
|
|
# compile and find the code size with the smallest configuration
|
|
- make clean size
|
|
OBJ="$(ls lfs*.o | tr '\n' ' ')"
|
|
CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
|
|
| tee sizes
|
|
|
|
# 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)
|
|
|
|
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
|
|
libc6-dev-armel-cross
|
|
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
|
|
libc6-dev-powerpc-cross
|
|
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 apt-get install
|
|
gcc-mips-linux-gnu
|
|
libc6-dev-mips-cross
|
|
qemu-user
|
|
- mips-linux-gnu-gcc --version
|
|
- qemu-mips -version
|
|
|
|
# self-host with littlefs-fuse for fuzz test
|
|
- stage: test
|
|
env:
|
|
- STAGE=test
|
|
- NAME=littlefs-fuse
|
|
if: branch !~ -prefix$
|
|
install:
|
|
- sudo apt-get install libfuse-dev
|
|
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2
|
|
- fusermount -V
|
|
- gcc --version
|
|
before_script:
|
|
# setup disk for littlefs-fuse
|
|
- rm -rf littlefs-fuse/littlefs/*
|
|
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
|
|
|
|
- mkdir mount
|
|
- sudo chmod a+rw /dev/loop0
|
|
- dd if=/dev/zero bs=512 count=4096 of=disk
|
|
- losetup /dev/loop0 disk
|
|
script:
|
|
# self-host test
|
|
- make -C littlefs-fuse
|
|
|
|
- littlefs-fuse/lfs --format /dev/loop0
|
|
- littlefs-fuse/lfs /dev/loop0 mount
|
|
|
|
- ls mount
|
|
- mkdir mount/littlefs
|
|
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
|
|
- cd mount/littlefs
|
|
- stat .
|
|
- ls -flh
|
|
- make -B test_dirs test_files QUIET=1
|
|
|
|
# self-host with littlefs-fuse for fuzz test
|
|
- stage: test
|
|
env:
|
|
- STAGE=test
|
|
- NAME=littlefs-migration
|
|
if: branch !~ -prefix$
|
|
install:
|
|
- sudo apt-get install libfuse-dev
|
|
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2 v2
|
|
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v1 v1
|
|
- fusermount -V
|
|
- gcc --version
|
|
before_script:
|
|
# setup disk for littlefs-fuse
|
|
- rm -rf v2/littlefs/*
|
|
- cp -r $(git ls-tree --name-only HEAD) v2/littlefs
|
|
|
|
- mkdir mount
|
|
- sudo chmod a+rw /dev/loop0
|
|
- dd if=/dev/zero bs=512 count=4096 of=disk
|
|
- losetup /dev/loop0 disk
|
|
script:
|
|
# compile v1 and v2
|
|
- make -C v1
|
|
- make -C v2
|
|
|
|
# run self-host test with v1
|
|
- v1/lfs --format /dev/loop0
|
|
- v1/lfs /dev/loop0 mount
|
|
|
|
- ls mount
|
|
- mkdir mount/littlefs
|
|
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
|
|
- cd mount/littlefs
|
|
- stat .
|
|
- ls -flh
|
|
- make -B test_dirs test_files QUIET=1
|
|
|
|
# attempt to migrate
|
|
- cd ../..
|
|
- fusermount -u mount
|
|
|
|
- v2/lfs --migrate /dev/loop0
|
|
- v2/lfs /dev/loop0 mount
|
|
|
|
# run self-host test with v2 right where we left off
|
|
- ls mount
|
|
- cd mount/littlefs
|
|
- stat .
|
|
- ls -flh
|
|
- make -B test_dirs test_files QUIET=1
|
|
|
|
# Automatically create releases
|
|
- stage: deploy
|
|
env:
|
|
- STAGE=deploy
|
|
- NAME=deploy
|
|
script:
|
|
- |
|
|
bash << 'SCRIPT'
|
|
set -ev
|
|
# Find version defined in lfs.h
|
|
LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
|
|
LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
|
|
LFS_VERSION_MINOR=$((0xffff & ($LFS_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$LFS_VERSION_MAJOR.$LFS_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)
|
|
LFS_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
|
|
LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
|
|
echo "VERSION $LFS_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')
|
|
[ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] || exit 0
|
|
# Create major branch
|
|
git branch v$LFS_VERSION_MAJOR HEAD
|
|
# Create major prefix branch
|
|
git config user.name "geky bot"
|
|
git config user.email "bot@geky.net"
|
|
git fetch https://github.com/$TRAVIS_REPO_SLUG.git \
|
|
--depth=50 v$LFS_VERSION_MAJOR-prefix || true
|
|
./scripts/prefix.py lfs$LFS_VERSION_MAJOR
|
|
git branch v$LFS_VERSION_MAJOR-prefix $( \
|
|
git commit-tree $(git write-tree) \
|
|
$(git rev-parse --verify -q FETCH_HEAD | sed -e 's/^/-p /') \
|
|
-p HEAD \
|
|
-m "Generated v$LFS_VERSION_MAJOR prefixes")
|
|
git reset --hard
|
|
# Update major version branches (vN and vN-prefix)
|
|
git push --atomic https://$GEKY_BOT_RELEASES@github.com/$TRAVIS_REPO_SLUG.git \
|
|
v$LFS_VERSION_MAJOR \
|
|
v$LFS_VERSION_MAJOR-prefix
|
|
# Build release notes
|
|
PREV=$(git tag --sort=-v:refname -l "v*" | head -1)
|
|
if [ ! -z "$PREV" ]
|
|
then
|
|
echo "PREV $PREV"
|
|
CHANGES=$(git log --oneline $PREV.. --grep='^Merge' --invert-grep)
|
|
printf "CHANGES\n%s\n\n" "$CHANGES"
|
|
fi
|
|
case ${GEKY_BOT_DRAFT:-minor} in
|
|
true) DRAFT=true ;;
|
|
minor) DRAFT=$(jq -R 'endswith(".0")' <<< "$LFS_VERSION") ;;
|
|
false) DRAFT=false ;;
|
|
esac
|
|
# Create the release and patch version tag (vN.N.N)
|
|
curl -f -u "$GEKY_BOT_RELEASES" -X POST \
|
|
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
|
|
-d "{
|
|
\"tag_name\": \"$LFS_VERSION\",
|
|
\"name\": \"${LFS_VERSION%.0}\",
|
|
\"target_commitish\": \"$TRAVIS_COMMIT\",
|
|
\"draft\": $DRAFT,
|
|
\"body\": $(jq -sR '.' <<< "$CHANGES")
|
|
}" #"
|
|
SCRIPT
|
|
|
|
# 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\"
|
|
}"
|
|
|
|
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\"
|
|
}"
|
|
|
|
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\"
|
|
}"
|
|
|
|
# Job control
|
|
stages:
|
|
- name: test
|
|
- name: deploy
|
|
if: branch = master AND type = push
|