Compare commits

..

13 Commits

Author SHA1 Message Date
Christopher Haster
ec4d8b68ad Changed release script to generate drafts 2018-10-20 12:34:41 -05:00
Christopher Haster
c7894a61e1 Added a handful of links to related projects
Interesting open-source projects that I've ran into around embedded
storage. May be interesting to others in the embedded space.

Added mklfs, SPIFFS, and Dhara.

Also a thanks to jolivepetrus for posting the mklfs tool he put
together.
2018-10-20 12:34:41 -05:00
Christopher Haster
195075819e Added 2GiB file size limit and EFBIG reporting
On disk, littlefs uses 32-bit integers to track file size. This sets a
theoretical limit of 4GiB for files.

However, the API passes file sizes around as signed numbers, with
negative values representing error codes. This means that not all of the
APIs will work with file sizes > 2GiB.

Because of related complications over in FUSE land, I've added the LFS_FILE_MAX
constant and proper error reporting if file writes/seeks exceed the 2GiB limit.
In v2 this will join the other constants that get stored in the
superblock to help portability. Since littlefs is targeting
microcontrollers, it's likely this will be a sufficient solution.

Note that it's still possible to enable partial-support for 4GiB files
by defining LFS_FILE_MAX during compilation. This will work for most of
the APIs, except lfs_file_seek, lfs_file_tell, and lfs_file_size.

We can also consider improving support for 4GiB files, by making seek a
bit more complicated and adding a lfs_file_stat function. I'll leave
this for a future improvement if there's interest.

Found by cgrozemuller
2018-10-20 12:34:23 -05:00
Christopher Haster
97d8d5e96a Fixed issue where a rename causes a split and pushes dir out of sync
The issue happens when a rename causes a split in the destination pair.
If the destination pair is the same as the source pair, this triggers the
logic to keep both pairs in sync. Unfortunately, this logic didn't work,
because the source entry still resides in the old source pair, unlike
the destination pair, which is now in the new pair created by the split.

The best fix for now is to refetch the source pair after the changes to the
destination pair. This isn't the most efficient solution, but fortunately
this bug has already been fixed in the revamped move logic in littlefs v2
(currently in progress).

Found by ohoc
2018-10-20 12:34:11 -05:00
Christopher Haster
0bb1f7af17 Modified release script to create notes only on minor releases
Before, release notes with a list of changes were created every
patch release. Unfortunately, it looks like this will create a lot of
noise on github, with a notification every patch release, which may be
as often as every time a PR is merged.

Rather than creating all of this noise for relatively uninteresting
changes, the script will now stick to simple tags, and create the
release notes only on minor releases.

I think this is what several of you were originally suggesting,
sorry about the journey, at least I learned a lot.
2018-09-29 12:31:27 -05:00
Christopher Haster
447d89cbd8 Merge pull request #109 from OTAkeys/pr/fix-sign-compare
Fix -Wsign-compare error
2018-09-29 12:29:54 -05:00
Vincent Dupont
28d2d96a83 Fix -Wsign-compare error 2018-09-29 11:33:19 -05:00
Christopher Haster
cb62bf2188 Fixed release script issue with fetching recent tags
Fetching all tags was triggering the pagination system inside the github
API. This prevent version tags from being found.

Modified to use the version tag prefix in the ref lookup, however this
still may cause an issue if there are still enough patch releases to trigger
pagination.

Simpleish solution is to grab the link header to jump to the last page,
since pagination results appear to be in sorted order.
2018-09-27 14:46:12 -05:00
Christopher Haster
646b1b5a6c Added -Wjump-misses-init and fixed uninitialized warnings 2018-09-26 18:58:54 -05:00
Christopher Haster
1b7a15599e Merge pull request #106 from conkerkh/master
If stats file doesn't exist lfs_emubd_create will fail.
2018-09-26 18:58:34 -05:00
Christopher Haster
e5a6938faf Fixed possible infinite loop in deorphan step
Normally, the linked-list of directory pairs should terminate at a null
pointer. However, it is possible if the filesystem is corrupted, that
that this linked-list forms a cycle.

This should never happen with littlefs's power resilience, but if it does
we should recover appropriately.

Modified lfs_deorphan to notice if we have a cycle and return
LFS_ERR_CORRUPT in that situation.

Found by kneko715
2018-09-26 18:58:11 -05:00
Chris
6ad544f3f3 If stats file doesn't exist lfs_emubd_create will fail.
This will create default stats file if it doesn't exist.
2018-09-26 18:24:58 -05:00
Christopher Haster
3419284689 Fixed issue with corruption due to different cache sizes
The lfs_cache_zero function that was recently added assumed a single cache
size, which is incorrect. This would cause a buffer overflow if
read_size != prog_size.

Since lfs_cache_zero is only used for scrubbing prog caches, the fix
here is to use lfs_cache_drop instead on read caches. Info in read
caches should never make its way to disk.

Found by nstcl
2018-09-04 13:57:22 -05:00
25 changed files with 1812 additions and 3649 deletions

View File

@@ -18,11 +18,10 @@ script:
- make test QUIET=1 - make test QUIET=1
# run tests with a few different configurations # 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=1 -DLFS_PROG_SIZE=1"
- 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=512 -DLFS_PROG_SIZE=512"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048" - make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048"
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" - make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
# compile and find the code size with the smallest configuration # compile and find the code size with the smallest configuration
@@ -103,7 +102,7 @@ jobs:
- NAME=littlefs-fuse - NAME=littlefs-fuse
install: install:
- sudo apt-get install libfuse-dev - sudo apt-get install libfuse-dev
- git clone --depth 1 https://github.com/geky/littlefs-fuse -b v2-alpha - git clone --depth 1 https://github.com/geky/littlefs-fuse
- fusermount -V - fusermount -V
- gcc --version - gcc --version
before_script: before_script:
@@ -139,12 +138,15 @@ jobs:
- LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3) - LFS_VERSION=$(grep -ox '#define LFS_VERSION .*' lfs.h | cut -d ' ' -f3)
- LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16))) - LFS_VERSION_MAJOR=$((0xffff & ($LFS_VERSION >> 16)))
- LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0))) - LFS_VERSION_MINOR=$((0xffff & ($LFS_VERSION >> 0)))
# Grab latests patch from repo tags, default to 0 # Grab latests patch from repo tags, default to 0, needs finagling to get past github's pagination api
- LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES" - PREV_URL=https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs - PREV_URL=$(curl -u "$GEKY_BOT_RELEASES" "$PREV_URL" -I
| jq 'map(.ref | match( | sed -n '/^Link/{s/.*<\(.*\)>; rel="last"/\1/;p;q0};$q1'
"refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$") || echo $PREV_URL)
.captures[].string | tonumber + 1) | max // 0') - 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 # We have our new version
- LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH" - LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
- echo "VERSION $LFS_VERSION" - echo "VERSION $LFS_VERSION"
@@ -155,24 +157,35 @@ jobs:
| jq -re '.sha') | jq -re '.sha')
if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ] if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
then then
# Build release notes # Create a simple tag
PREV=$(git tag --sort=-v:refname -l "v*" | 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 \ curl -f -u "$GEKY_BOT_RELEASES" -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \ https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{ -d "{
\"tag_name\": \"$LFS_VERSION\", \"ref\": \"refs/tags/$LFS_VERSION\",
\"target_commitish\": \"$TRAVIS_COMMIT\", \"sha\": \"$TRAVIS_COMMIT\"
\"name\": \"${LFS_VERSION%.0}\",
\"body\": $(jq -sR '.' <<< "$CHANGES")
}" }"
# Minor release?
if [[ "$LFS_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\": \"$LFS_VERSION\",
\"name\": \"${LFS_VERSION%.0}\",
\"draft\": true,
\"body\": $(jq -sR '.' <<< "$CHANGES")
}"
fi
fi fi
# Manage statuses # Manage statuses

View File

@@ -25,7 +25,8 @@ ifdef WORD
override CFLAGS += -m$(WORD) override CFLAGS += -m$(WORD)
endif endif
override CFLAGS += -I. override CFLAGS += -I.
override CFLAGS += -std=c99 -Wall -pedantic -Wshadow -Wunused-parameter override CFLAGS += -std=c99 -Wall -pedantic
override CFLAGS += -Wshadow -Wunused-parameter -Wjump-misses-init -Wsign-compare
all: $(TARGET) all: $(TARGET)
@@ -37,8 +38,7 @@ size: $(OBJ)
.SUFFIXES: .SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate \ test: test_format test_dirs test_files test_seek test_truncate \
test_entries test_interspersed test_alloc test_paths test_attrs \ test_interspersed test_alloc test_paths test_orphan test_move test_corrupt
test_move test_orphan test_corrupt
@rm test.c @rm test.c
test_%: tests/test_%.sh test_%: tests/test_%.sh

View File

@@ -175,3 +175,18 @@ handy.
[littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for [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. 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). 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.

View File

@@ -19,40 +19,6 @@
#include <inttypes.h> #include <inttypes.h>
// Emulated block device utils
static inline void lfs_emubd_tole32(lfs_emubd_t *emu) {
emu->cfg.read_size = lfs_tole32(emu->cfg.read_size);
emu->cfg.prog_size = lfs_tole32(emu->cfg.prog_size);
emu->cfg.block_size = lfs_tole32(emu->cfg.block_size);
emu->cfg.block_count = lfs_tole32(emu->cfg.block_count);
emu->stats.read_count = lfs_tole32(emu->stats.read_count);
emu->stats.prog_count = lfs_tole32(emu->stats.prog_count);
emu->stats.erase_count = lfs_tole32(emu->stats.erase_count);
for (int i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]);
}
}
static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) {
emu->cfg.read_size = lfs_fromle32(emu->cfg.read_size);
emu->cfg.prog_size = lfs_fromle32(emu->cfg.prog_size);
emu->cfg.block_size = lfs_fromle32(emu->cfg.block_size);
emu->cfg.block_count = lfs_fromle32(emu->cfg.block_count);
emu->stats.read_count = lfs_fromle32(emu->stats.read_count);
emu->stats.prog_count = lfs_fromle32(emu->stats.prog_count);
emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count);
for (int i = 0; i < sizeof(emu->history.blocks) /
sizeof(emu->history.blocks[0]); i++) {
emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]);
}
}
// Block device emulated on existing filesystem // Block device emulated on existing filesystem
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
lfs_emubd_t *emu = cfg->context; lfs_emubd_t *emu = cfg->context;
@@ -80,31 +46,17 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
} }
// Load stats to continue incrementing // Load stats to continue incrementing
snprintf(emu->child, LFS_NAME_MAX, ".stats"); snprintf(emu->child, LFS_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);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f); FILE *f = fopen(emu->path, "r");
if (err) { if (!f && errno != ENOENT) {
return -errno; return -errno;
}
} }
// Load history if (errno == ENOENT) {
snprintf(emu->child, LFS_NAME_MAX, ".history"); memset(&emu->stats, 0x0, sizeof(emu->stats));
f = fopen(emu->path, "r");
if (!f) {
memset(&emu->history, 0, sizeof(emu->history));
} else { } else {
size_t res = fread(&emu->history, sizeof(emu->history), 1, f); size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) { if (res < 1) {
return -errno; return -errno;
} }
@@ -214,13 +166,6 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
return -errno; 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; emu->stats.prog_count += 1;
return 0; return 0;
} }
@@ -266,15 +211,13 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context; lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup // Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, ".config"); snprintf(emu->child, LFS_NAME_MAX, "config");
FILE *f = fopen(emu->path, "w"); FILE *f = fopen(emu->path, "w");
if (!f) { if (!f) {
return -errno; return -errno;
} }
lfs_emubd_tole32(emu);
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) { if (res < 1) {
return -errno; return -errno;
} }
@@ -284,33 +227,13 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
return -errno; return -errno;
} }
snprintf(emu->child, LFS_NAME_MAX, ".stats"); snprintf(emu->child, LFS_NAME_MAX, "stats");
f = fopen(emu->path, "w"); f = fopen(emu->path, "w");
if (!f) { if (!f) {
return -errno; return -errno;
} }
lfs_emubd_tole32(emu);
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
snprintf(emu->child, LFS_NAME_MAX, ".history");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
res = fwrite(&emu->history, sizeof(emu->history), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) { if (res < 1) {
return -errno; return -errno;
} }

View File

@@ -45,10 +45,6 @@ typedef struct lfs_emubd {
uint64_t erase_count; uint64_t erase_count;
} stats; } stats;
struct {
lfs_block_t blocks[4];
} history;
struct { struct {
uint32_t read_size; uint32_t read_size;
uint32_t prog_size; uint32_t prog_size;

3760
lfs.c

File diff suppressed because it is too large Load Diff

390
lfs.h
View File

@@ -21,14 +21,14 @@ extern "C"
// Software library version // Software library version
// Major (top-nibble), incremented on backwards incompatible changes // Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions // Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00020000 #define LFS_VERSION 0x00010007
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) #define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) #define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
// Version of On-disk data structures // Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes // Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions // Minor (bottom-nibble), incremented on feature additions
#define LFS_DISK_VERSION 0x00020000 #define LFS_DISK_VERSION 0x00010001
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) #define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16))
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) #define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
@@ -44,95 +44,57 @@ typedef int32_t lfs_soff_t;
typedef uint32_t lfs_block_t; typedef uint32_t lfs_block_t;
// Maximum size of all attributes per file in bytes, may be redefined but a // Max name size in bytes
// a smaller LFS_ATTR_MAX has no benefit. Stored in 12-bits and limited
// to <= 0xfff. Stored in superblock and must be respected by other
// littlefs drivers.
#ifndef LFS_ATTR_MAX
#define LFS_ATTR_MAX 0xfff
#endif
// Maximum name size in bytes, may be redefined to reduce the size of the
// info struct. Limited to <= LFS_ATTR_MAX. Stored in superblock and must
// be respected by other littlefs drivers.
#ifndef LFS_NAME_MAX #ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 0xff #define LFS_NAME_MAX 255
#endif #endif
// Maximum inline file size in bytes. Large inline files require a larger // Max file size in bytes
// cache size, but if a file can be inline it does not need its own data #ifndef LFS_FILE_MAX
// block. Limited to <= LFS_ATTR_MAX and <= cache_size. Stored in superblock #define LFS_FILE_MAX 2147483647
// and must be respected by other littlefs drivers.
#ifndef LFS_INLINE_MAX
#define LFS_INLINE_MAX 0xfff
#endif #endif
// Possible error codes, these are negative to allow // Possible error codes, these are negative to allow
// valid positive return values // valid positive return values
enum lfs_error { enum lfs_error {
LFS_ERR_OK = 0, // No error LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -84, // Corrupted LFS_ERR_CORRUPT = -52, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_INVAL = -22, // Invalid parameter LFS_ERR_FBIG = -27, // File too large
LFS_ERR_NOSPC = -28, // No space left on device LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOMEM = -12, // No more memory available LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NAMETOOLONG = -36, // File name too long LFS_ERR_NOMEM = -12, // No more memory available
}; };
// File types // File types
enum lfs_type { enum lfs_type {
// file types LFS_TYPE_REG = 0x11,
LFS_TYPE_REG = 0x001, LFS_TYPE_DIR = 0x22,
LFS_TYPE_DIR = 0x002, LFS_TYPE_SUPERBLOCK = 0x2e,
// internally used types
LFS_TYPE_USER = 0x100,
LFS_TYPE_SUPERBLOCK = 0x011,
LFS_TYPE_ROOT = 0x012,
LFS_TYPE_NAME = 0x000,
LFS_TYPE_DELETE = 0x030,
LFS_TYPE_STRUCT = 0x040,
LFS_TYPE_GLOBALS = 0x080,
LFS_TYPE_TAIL = 0x0c0,
LFS_TYPE_SOFTTAIL = 0x0c0,
LFS_TYPE_HARDTAIL = 0x0c1,
LFS_TYPE_CRC = 0x0f0,
LFS_TYPE_DIRSTRUCT = 0x040,
LFS_TYPE_INLINESTRUCT = 0x041,
LFS_TYPE_CTZSTRUCT = 0x042,
// internal chip sources
LFS_FROM_REGION = 0x000,
LFS_FROM_DISK = 0x200,
LFS_FROM_MOVE = 0x050,
LFS_FROM_ATTRS = 0x060,
LFS_FROM_SUPERBLOCK = 0x070,
}; };
// File open flags // File open flags
enum lfs_open_flags { enum lfs_open_flags {
// open flags // open flags
LFS_O_RDONLY = 1, // Open a file as read only LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write 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_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags // internally used flags
LFS_F_DIRTY = 0x010000, // File does not match storage LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x020000, // File has been written since last flush LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x040000, // File has been read since last flush LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x080000, // An error occured during write LFS_F_ERRED = 0x80000, // An error occured during write
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
}; };
// File seek flags // File seek flags
@@ -170,34 +132,26 @@ struct lfs_config {
// are propogated to the user. // are propogated to the user.
int (*sync)(const struct lfs_config *c); int (*sync)(const struct lfs_config *c);
// Minimum size of a block read. All read operations will be a // Minimum size of a block read. This determines the size of read buffers.
// multiple of this value. // This may be larger than the physical read size to improve performance
// by caching more of the block device.
lfs_size_t read_size; lfs_size_t read_size;
// Minimum size of a block program. All program operations will be a // Minimum size of a block program. This determines the size of program
// multiple of this value. // 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; lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and // Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be // may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block. // kept small as each file currently takes up an entire block.
// Must be a multiple of the read, program, and cache sizes. // Must be a multiple of the program size.
lfs_size_t block_size; lfs_size_t block_size;
// Number of erasable blocks on the device. // Number of erasable blocks on the device.
lfs_size_t block_count; lfs_size_t block_count;
// Number of erase cycles before we should move data to another block.
// May be zero to never move data, 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.
// This determines the size of the read cache, the program cache, and a
// cache per file. Larger caches can improve performance by storing more
// data. Must be a multiple of the read and program sizes.
lfs_size_t cache_size;
// Number of blocks to lookahead during block allocation. A larger // Number of blocks to lookahead during block allocation. A larger
// lookahead reduces the number of passes required to allocate a block. // 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 // The lookahead buffer requires only 1 bit per block so it can be quite
@@ -214,24 +168,16 @@ struct lfs_config {
// lookahead block. // lookahead block.
void *lookahead_buffer; void *lookahead_buffer;
// Optional upper limit on file attributes in bytes. No downside for larger // Optional, statically allocated buffer for files. Must be program sized.
// attributes size but must be less than LFS_ATTR_MAX. Defaults to // If enabled, only one file may be opened at a time.
// LFS_ATTR_MAX when zero.Stored in superblock and must be respected by void *file_buffer;
// other littlefs drivers. };
lfs_size_t attr_max;
// Optional upper limit on length of file names in bytes. No downside for // Optional configuration provided during lfs_file_opencfg
// larger names except the size of the info struct which is controlled by struct lfs_file_config {
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in // Optional, statically allocated buffer for files. Must be program sized.
// superblock and must be respected by other littlefs drivers. // If NULL, malloc will be used by default.
lfs_size_t name_max; void *buffer;
// Optional upper limit on inlined files in bytes. Large inline files
// require a larger cache size, but if a file can be inlined it does not
// need its own data block. Must be smaller than cache_size and less than
// LFS_INLINE_MAX. Defaults to min(LFS_INLINE_MAX, read_size) when zero.
// Stored in superblock and must be respected by other littlefs drivers.
lfs_size_t inline_max;
}; };
// File info structure // File info structure
@@ -246,125 +192,77 @@ struct lfs_info {
char name[LFS_NAME_MAX+1]; char name[LFS_NAME_MAX+1];
}; };
// Custom attribute structure
struct lfs_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 LFS_ATTR_MAX
lfs_size_t size;
// Pointer to next attribute in linked list
struct lfs_attr *next;
};
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_config {
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
// Optional, linked list of custom attributes related to the file. If the
// file is opened with read access, the attributes will be read from
// 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 LFS_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 lfs_attr *attrs;
};
/// littlefs data structures /// /// littlefs data structures ///
typedef struct lfs_mattr { typedef struct lfs_entry {
int32_t tag; lfs_off_t off;
const void *buffer;
const struct lfs_mattr *next; struct lfs_disk_entry {
} lfs_mattr_t; 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 { typedef struct lfs_cache {
lfs_block_t block; lfs_block_t block;
lfs_off_t off; lfs_off_t off;
lfs_size_t size;
uint8_t *buffer; uint8_t *buffer;
} lfs_cache_t; } lfs_cache_t;
typedef union lfs_global {
uint32_t u32[3];
struct {
lfs_block_t movepair[2];
uint16_t moveid;
bool deorphaned;
} s;
} lfs_global_t;
typedef struct lfs_mdir {
lfs_block_t pair[2];
uint32_t rev;
uint32_t etag;
lfs_off_t off;
uint16_t count;
bool erased;
bool split;
lfs_block_t tail[2];
lfs_global_t locals;
} lfs_mdir_t;
typedef struct lfs_mlist {
struct lfs_mlist *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
} lfs_mlist_t;
typedef struct lfs_dir {
struct lfs_dir *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
lfs_off_t pos;
lfs_block_t head[2];
} lfs_dir_t;
typedef struct lfs_file { typedef struct lfs_file {
struct lfs_file *next; struct lfs_file *next;
uint16_t id; lfs_block_t pair[2];
uint8_t type; lfs_off_t poff;
lfs_mdir_t m;
struct lfs_ctz { lfs_block_t head;
lfs_block_t head; lfs_size_t size;
lfs_size_t size;
} ctz;
const struct lfs_file_config *cfg;
uint32_t flags; uint32_t flags;
lfs_off_t pos; lfs_off_t pos;
lfs_block_t block; lfs_block_t block;
lfs_off_t off; lfs_off_t off;
lfs_cache_t cache; lfs_cache_t cache;
const struct lfs_file_config *cfg;
} lfs_file_t; } 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 { typedef struct lfs_superblock {
char magic[8]; lfs_off_t off;
uint32_t version;
lfs_size_t block_size; struct lfs_disk_superblock {
lfs_size_t block_count; uint8_t type;
uint8_t elen;
lfs_size_t attr_max; uint8_t alen;
lfs_size_t name_max; uint8_t nlen;
lfs_size_t inline_max; lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
char magic[8];
} d;
} lfs_superblock_t; } lfs_superblock_t;
typedef struct lfs_free { typedef struct lfs_free {
@@ -377,23 +275,18 @@ typedef struct lfs_free {
// The littlefs type // The littlefs type
typedef struct lfs { 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 rcache;
lfs_cache_t pcache; lfs_cache_t pcache;
lfs_block_t root[2];
lfs_mlist_t *mlist;
uint32_t seed;
lfs_global_t globals;
lfs_global_t locals;
lfs_free_t free; lfs_free_t free;
bool deorphaned;
const struct lfs_config *cfg; bool moving;
lfs_size_t block_size;
lfs_size_t block_count;
lfs_size_t attr_max;
lfs_size_t name_max;
lfs_size_t inline_max;
} lfs_t; } lfs_t;
@@ -446,31 +339,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
// Get a custom attribute
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_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.
//
// 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.
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created, and setting the size of an attribute to zero deletes
// the attribute.
//
// Returns a negative error code on failure.
int lfs_setattr(lfs_t *lfs, const char *path,
uint8_t type, const void *buffer, lfs_size_t size);
/// File operations /// /// File operations ///
@@ -605,15 +473,7 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
/// Filesystem-level filesystem operations /// Miscellaneous littlefs specific 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.
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// Traverse through all blocks in use by the filesystem // Traverse through all blocks in use by the filesystem
// //
@@ -622,36 +482,16 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// blocks are in use or how much of the storage is available. // blocks are in use or how much of the storage is available.
// //
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
// Get custom attributes on the filesystem // Prunes any recoverable errors that may have occured in the filesystem
// //
// Custom attributes are uniquely identified by an 8-bit type and limited // Not needed to be called by user unless an operation is interrupted
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than // but the filesystem is still mounted. This is already called on first
// the buffer, it will be padded with zeros. If the stored attribute is larger, // allocation.
// then it will be silently truncated.
//
// Note, filesystem-level attributes are not available for wear-leveling
//
// 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.
lfs_ssize_t lfs_fs_getattr(lfs_t *lfs,
uint8_t type, void *buffer, lfs_size_t size);
// Set custom attributes on the filesystem
//
// Custom attributes are uniquely identified by an 8-bit type and limited
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
// implicitly created, and setting the size of an attribute to zero deletes
// the attribute.
//
// Note, filesystem-level attributes are not available for wear-leveling
// //
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_fs_setattr(lfs_t *lfs, int lfs_deorphan(lfs_t *lfs);
uint8_t type, const void *buffer, lfs_size_t size);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -11,7 +11,7 @@
// Software CRC implementation with small lookup table // Software CRC implementation with small lookup table
uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size) { void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = { static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
@@ -22,11 +22,9 @@ uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size) {
const uint8_t *data = buffer; const uint8_t *data = buffer;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; *crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
} }
return crc;
} }

View File

@@ -11,8 +11,8 @@
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). // LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
// //
// If LFS_CONFIG is used, none of the default utils will be emitted and must be // If LFS_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 lfs_util.h // provided by the config file. To start I would suggest copying lfs_util.h and
// and modifying as needed. // modifying as needed.
#ifdef LFS_CONFIG #ifdef LFS_CONFIG
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) #define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x #define LFS_STRINGIZE2(x) #x
@@ -23,7 +23,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#ifndef LFS_NO_MALLOC #ifndef LFS_NO_MALLOC
#include <stdlib.h> #include <stdlib.h>
@@ -88,15 +87,6 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b; return (a < b) ? a : b;
} }
// Align to nearest multiple of a size
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
return a - (a % alignment);
}
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
}
// Find the next smallest power of 2 less than or equal to a // Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) { static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
@@ -140,7 +130,7 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b); return (int)(unsigned)(a - b);
} }
// Convert between 32-bit little-endian and native order // Convert from 32-bit little-endian to native order
static inline uint32_t lfs_fromle32(uint32_t a) { static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \ #if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
@@ -160,34 +150,13 @@ static inline uint32_t lfs_fromle32(uint32_t a) {
#endif #endif
} }
// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t a) { static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a); return lfs_fromle32(a);
} }
// Convert between 16-bit little-endian and native order
static inline uint16_t lfs_fromle16(uint16_t a) {
#if !defined(LFS_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(LFS_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_bswap16(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8);
#endif
}
static inline uint16_t lfs_tole16(uint16_t a) {
return lfs_fromle16(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7 // Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc32(uint32_t crc, const void *buffer, size_t size); void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs // Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs_malloc(size_t size) { static inline void *lfs_malloc(size_t size) {

View File

@@ -1,43 +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 = 0
while True:
try:
ntag, = struct.unpack('<I', file.read(4))
except struct.error:
break
tag ^= ntag
file.seek(tag & 0xfff, os.SEEK_CUR)
# lob off last 3 bytes
file.seek(-((tag & 0xfff) + 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,98 +0,0 @@
#!/usr/bin/env python2
import struct
import binascii
TYPES = {
(0x1ff, 0x001): 'reg',
(0x1ff, 0x002): 'dir',
(0x1ff, 0x011): 'superblock',
(0x1ff, 0x012): 'root',
(0x1ff, 0x030): 'delete',
(0x1f0, 0x080): 'globals',
(0x1ff, 0x0c0): 'tail soft',
(0x1ff, 0x0c1): 'tail hard',
(0x1ff, 0x0f0): 'crc',
(0x1ff, 0x040): 'struct dir',
(0x1ff, 0x041): 'struct inline',
(0x1ff, 0x042): 'struct ctz',
(0x100, 0x100): 'attr',
}
def typeof(type):
for prefix in range(9):
mask = 0x1ff & ~((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:
pass
print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True))
# go through each tag, print useful information
print "%-4s %-8s %-14s %3s %3s %s" % (
'off', 'tag', 'type', 'id', 'len', 'dump')
tag = 0
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 & 0x7fc00000) >> 22
id = (tag & 0x003ff000) >> 12
size = (tag & 0x00000fff) >> 0
data = file.read(size)
if type == 0x0f0:
crc = binascii.crc32(data[:4], crc)
else:
crc = binascii.crc32(data, crc)
print '%04x: %08x %-14s %3s %3d %-23s %-8s' % (
off, tag,
typeof(type) + (' bad!' if type == 0x0f0 and ~crc else ''),
id if id != 0x3ff else '.', size,
' '.join('%02x' % ord(c) for c in data[:8]),
''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8]))
off += tag & 0xfff
if type == 0x0f0:
crc = 0
if __name__ == "__main__":
import sys
main(*sys.argv[1:])

View File

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

View File

@@ -66,7 +66,7 @@ uintmax_t test;
#endif #endif
#ifndef LFS_PROG_SIZE #ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE LFS_READ_SIZE #define LFS_PROG_SIZE 16
#endif #endif
#ifndef LFS_BLOCK_SIZE #ifndef LFS_BLOCK_SIZE
@@ -77,14 +77,6 @@ uintmax_t test;
#define LFS_BLOCK_COUNT 1024 #define LFS_BLOCK_COUNT 1024
#endif #endif
#ifndef LFS_BLOCK_CYCLES
#define LFS_BLOCK_CYCLES 1024
#endif
#ifndef LFS_CACHE_SIZE
#define LFS_CACHE_SIZE 64
#endif
#ifndef LFS_LOOKAHEAD #ifndef LFS_LOOKAHEAD
#define LFS_LOOKAHEAD 128 #define LFS_LOOKAHEAD 128
#endif #endif
@@ -96,13 +88,11 @@ const struct lfs_config cfg = {{
.erase = &lfs_emubd_erase, .erase = &lfs_emubd_erase,
.sync = &lfs_emubd_sync, .sync = &lfs_emubd_sync,
.read_size = LFS_READ_SIZE, .read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE, .prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE, .block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT, .block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES, .lookahead = LFS_LOOKAHEAD,
.cache_size = LFS_CACHE_SIZE,
.lookahead = LFS_LOOKAHEAD,
}}; }};

View File

@@ -10,7 +10,7 @@ def generate(test):
template = file.read() template = file.read()
lines = [] 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) match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE)
if match: if match:
tab, test, expect = match.groups() tab, test, expect = match.groups()

View File

@@ -32,18 +32,18 @@ lfs_alloc_singleproc() {
tests/test.py << TEST tests/test.py << TEST
const char *names[] = {"bacon", "eggs", "pancakes"}; const char *names[] = {"bacon", "eggs", "pancakes"};
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
sprintf((char*)buffer, "$1/%s", names[n]); sprintf((char*)buffer, "$1/%s", names[n]);
lfs_file_open(&lfs, &file[n], (char*)buffer, lfs_file_open(&lfs, &file[n], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0; LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
} }
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
size = strlen(names[n]); size = strlen(names[n]);
for (int i = 0; i < $SIZE; i++) { for (int i = 0; i < $SIZE; i++) {
lfs_file_write(&lfs, &file[n], names[n], size) => size; lfs_file_write(&lfs, &file[n], names[n], size) => size;
} }
} }
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) { for (unsigned n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
lfs_file_close(&lfs, &file[n]) => 0; lfs_file_close(&lfs, &file[n]) => 0;
} }
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -194,98 +194,55 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size; lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0; memcmp(buffer, "exhaustion", size) => 0;
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Dir exhaustion test ---" echo "--- Dir exhaustion test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
lfs_mkdir(&lfs, "exhaustiondir") => 0;
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); for (lfs_size_t i = 0;
int count = 0; i < (cfg.block_count-6)*(cfg.block_size-8);
int err; i += size) {
while (true) {
err = lfs_file_write(&lfs, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
// see if dir fits with max file size
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0; lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0; lfs_remove(&lfs, "exhaustiondir") => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// see if dir fits with > max file size lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); size = strlen("blahblahblahblah");
for (int i = 0; i < count+1; i++) { 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; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Chained dir exhaustion test ---" echo "--- Chained dir exhaustion test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
lfs_mkdir(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); for (lfs_size_t i = 0;
int count = 0; i < (cfg.block_count-24)*(cfg.block_size-8);
int err; i += size) {
while (true) {
err = lfs_file_write(&lfs, &file[0], buffer, size);
if (err < 0) {
break;
}
count += 1;
}
err => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_remove(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs_remove(&lfs, (char*)buffer) => 0;
}
// see that chained dir fails
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
for (int i = 0; i < count+1; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
lfs_file_sync(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i); sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
@@ -294,25 +251,19 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC; lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
// shorten file to try a second chained dir lfs_remove(&lfs, "exhaustion") => 0;
while (true) { lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
err = lfs_mkdir(&lfs, "exhaustiondir"); size = strlen("blahblahblahblah");
if (err != LFS_ERR_NOSPC) { memcpy(buffer, "blahblahblahblah", size);
break; for (lfs_size_t i = 0;
} i < (cfg.block_count-26)*(cfg.block_size-8);
i += size) {
lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]); lfs_file_write(&lfs, &file[0], buffer, size) => size;
filesize > 0 => true;
lfs_file_truncate(&lfs, &file[0], filesize - size) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
} }
err => 0;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
TEST TEST
echo "--- Split dir test ---" echo "--- Split dir test ---"
@@ -323,19 +274,16 @@ TEST
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
// create one block hole for half a directory // create one block whole for half a directory
lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file[0], "bump", LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2;
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => cfg.block_size;
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT); lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < (cfg.block_count-4)*(cfg.block_size-8); i < (cfg.block_count-6)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -347,10 +295,7 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "splitdir") => 0; lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file[0], "splitdir/bump", lfs_file_open(&lfs, &file[0], "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0; LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) { lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC;
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -369,7 +314,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -380,7 +325,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -398,7 +343,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -412,7 +357,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -432,7 +377,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2)*(cfg.block_size-8); i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -443,7 +388,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2+1)/2)*(cfg.block_size-8); i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
@@ -461,7 +406,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah"); size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size); memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0; for (lfs_size_t i = 0;
i < ((cfg.block_count-2)/2 - 1)*(cfg.block_size-8); i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8);
i += size) { i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }

View File

@@ -1,262 +0,0 @@
#!/bin/bash
set -eu
echo "=== Attr tests ==="
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "hello") => 0;
lfs_file_open(&lfs, &file[0], "hello/hello",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file[0], "hello", strlen("hello"))
=> strlen("hello");
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_setattr(&lfs, "hello", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "hello", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "hello", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "", 0) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 0;
lfs_getattr(&lfs, "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;
lfs_setattr(&lfs, "hello", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 6;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_setattr(&lfs, "hello", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 3;
lfs_getattr(&lfs, "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;
lfs_setattr(&lfs, "hello", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "hello", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_getattr(&lfs, "hello", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "hello", 'B', buffer+4, 9) => 9;
lfs_getattr(&lfs, "hello", 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get fs attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_fs_setattr(&lfs, 'A', "aaaa", 4) => 0;
lfs_fs_setattr(&lfs, 'B', "bbbbbb", 6) => 0;
lfs_fs_setattr(&lfs, 'C', "ccccc", 5) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 6;
lfs_fs_getattr(&lfs, 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_fs_setattr(&lfs, 'B', "", 0) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 0;
lfs_fs_getattr(&lfs, '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;
lfs_fs_setattr(&lfs, 'B', "dddddd", 6) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 6;
lfs_fs_getattr(&lfs, 'C', buffer+10, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
lfs_fs_setattr(&lfs, 'B', "eee", 3) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 3;
lfs_fs_getattr(&lfs, '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;
lfs_fs_setattr(&lfs, 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_fs_setattr(&lfs, 'B', "fffffffff", 9) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 6) => 9;
lfs_fs_getattr(&lfs, 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_fs_getattr(&lfs, 'A', buffer, 4) => 4;
lfs_fs_getattr(&lfs, 'B', buffer+4, 9) => 9;
lfs_fs_getattr(&lfs, 'C', buffer+13, 5) => 5;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Set/get file attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a1 = {'A', buffer, 4};
struct lfs_attr b1 = {'B', buffer+4, 6, &a1};
struct lfs_attr c1 = {'C', buffer+10, 5, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer, "aaaa", 4);
memcpy(buffer+4, "bbbbbb", 6);
memcpy(buffer+10, "ccccc", 5);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "bbbbbb", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
b1.size = 0;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &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;
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "dddddd", 6);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "dddddd", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
b1.size = 3;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
memcpy(buffer+4, "eee", 3);
lfs_file_close(&lfs, &file[0]) => 0;
memset(buffer, 0, 15);
b1.size = 6;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "eee\0\0\0", 6) => 0;
memcmp(buffer+10, "ccccc", 5) => 0;
a1.size = LFS_ATTR_MAX+1;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1)
=> LFS_ERR_NOSPC;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDWR, &cfg2) => 0;
memcpy(buffer+4, "fffffffff", 9);
lfs_file_close(&lfs, &file[0]) => 0;
a1.size = 4;
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg1) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a2 = {'A', buffer, 4};
struct lfs_attr b2 = {'B', buffer+4, 9, &a2};
struct lfs_attr c2 = {'C', buffer+13, 5, &b2};
struct lfs_file_config cfg2 = {.attrs = &c2};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_RDONLY, &cfg2) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
memcmp(buffer, "aaaa", 4) => 0;
memcmp(buffer+4, "fffffffff", 9) => 0;
memcmp(buffer+13, "ccccc", 5) => 0;
lfs_file_open(&lfs, &file[0], "hello/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, sizeof(buffer)) => strlen("hello");
memcmp(buffer, "hello", strlen("hello")) => 0;
lfs_file_close(&lfs, &file[0]);
lfs_unmount(&lfs) => 0;
TEST
echo "--- Deferred file attributes ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
struct lfs_attr a1 = {'B', "gggg", 4};
struct lfs_attr b1 = {'C', "", 0, &a1};
struct lfs_attr c1 = {'D', "hhhh", 4, &b1};
struct lfs_file_config cfg1 = {.attrs = &c1};
lfs_file_opencfg(&lfs, &file[0], "hello/hello", LFS_O_WRONLY, &cfg1) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 9;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 5;
lfs_getattr(&lfs, "hello/hello", 'D', buffer+18, 9) => 0;
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;
lfs_file_sync(&lfs, &file[0]) => 0;
lfs_getattr(&lfs, "hello/hello", 'B', buffer, 9) => 4;
lfs_getattr(&lfs, "hello/hello", 'C', buffer+9, 9) => 0;
lfs_getattr(&lfs, "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;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -71,25 +71,24 @@ echo "--- Sanity check ---"
rm -rf blocks rm -rf blocks
lfs_mktree lfs_mktree
lfs_chktree lfs_chktree
BLOCKS="$(ls blocks | grep -vw '[01]')"
echo "--- Block corruption ---" echo "--- Block corruption ---"
for b in $BLOCKS for i in {0..33}
do do
rm -rf blocks rm -rf blocks
mkdir blocks mkdir blocks
ln -s /dev/zero blocks/$b ln -s /dev/zero blocks/$(printf '%x' $i)
lfs_mktree lfs_mktree
lfs_chktree lfs_chktree
done done
echo "--- Block persistance ---" echo "--- Block persistance ---"
for b in $BLOCKS for i in {0..33}
do do
rm -rf blocks rm -rf blocks
mkdir blocks mkdir blocks
lfs_mktree lfs_mktree
chmod a-w blocks/$b chmod a-w blocks/$(printf '%x' $i)
lfs_mktree lfs_mktree
lfs_chktree lfs_chktree
done done
@@ -97,7 +96,7 @@ done
echo "--- Big region corruption ---" echo "--- Big region corruption ---"
rm -rf blocks rm -rf blocks
mkdir blocks mkdir blocks
for i in {2..512} for i in {2..255}
do do
ln -s /dev/zero blocks/$(printf '%x' $i) ln -s /dev/zero blocks/$(printf '%x' $i)
done done
@@ -107,7 +106,7 @@ lfs_chktree
echo "--- Alternating corruption ---" echo "--- Alternating corruption ---"
rm -rf blocks rm -rf blocks
mkdir blocks mkdir blocks
for i in {2..1024..2} for i in {2..511..2}
do do
ln -s /dev/zero blocks/$(printf '%x' $i) ln -s /dev/zero blocks/$(printf '%x' $i)
done done

View File

@@ -326,13 +326,42 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Multi-block rename ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i);
sprintf((char*)wbuffer, "cactus/tedd%d", i);
lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
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 => LFS_TYPE_DIR;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0;
info.type => LFS_TYPE_DIR;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "tedd%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS_TYPE_DIR;
}
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Multi-block remove ---" echo "--- Multi-block remove ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY; lfs_remove(&lfs, "cactus") => LFS_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "cactus/test%d", i); sprintf((char*)buffer, "cactus/tedd%d", i);
lfs_remove(&lfs, (char*)buffer) => 0; lfs_remove(&lfs, (char*)buffer) => 0;
} }
@@ -391,13 +420,43 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Multi-block rename with files ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%d", i);
sprintf((char*)wbuffer, "prickly-pear/tedd%d", i);
lfs_rename(&lfs, (char*)buffer, (char*)wbuffer) => 0;
}
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "prickly-pear") => 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;
for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "tedd%d", i);
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, (char*)buffer) => 0;
info.type => LFS_TYPE_REG;
info.size => 6;
}
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Multi-block remove with files ---" echo "--- Multi-block remove with files ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY; lfs_remove(&lfs, "prickly-pear") => LFS_ERR_NOTEMPTY;
for (int i = 0; i < $LARGESIZE; i++) { for (int i = 0; i < $LARGESIZE; i++) {
sprintf((char*)buffer, "prickly-pear/test%d", i); sprintf((char*)buffer, "prickly-pear/tedd%d", i);
lfs_remove(&lfs, (char*)buffer) => 0; lfs_remove(&lfs, (char*)buffer) => 0;
} }

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;
lfs_file_open(&lfs, &file[0], "$1", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
TEST
}
function write_file {
cat << TEST
size = $2;
lfs_file_open(&lfs, &file[0], "$1",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
TEST
}
echo "--- Entry grow test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &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)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry shrink test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &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)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry spill test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &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)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry push spill test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &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)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry push spill two test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &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)
lfs_unmount(&lfs) => 0;
TEST
echo "--- Entry drop test ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
$(write_file "hi0" 200)
$(write_file "hi1" 200)
$(write_file "hi2" 200)
$(write_file "hi3" 200)
lfs_remove(&lfs, "hi1") => 0;
lfs_stat(&lfs, "hi1", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi2" 200)
$(read_file "hi3" 200)
lfs_remove(&lfs, "hi2") => 0;
lfs_stat(&lfs, "hi2", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
$(read_file "hi3" 200)
lfs_remove(&lfs, "hi3") => 0;
lfs_stat(&lfs, "hi3", &info) => LFS_ERR_NOENT;
$(read_file "hi0" 200)
lfs_remove(&lfs, "hi0") => 0;
lfs_stat(&lfs, "hi0", &info) => LFS_ERR_NOENT;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Create too big ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Resize too big ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'm', 200);
buffer[200] = '\0';
size = 40;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 40;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
memset(wbuffer, 'c', size);
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
lfs_file_close(&lfs, &file[0]) => 0;
size = 400;
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
memcmp(rbuffer, wbuffer, size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---"
tests/stats.py

View File

@@ -9,6 +9,14 @@ tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
tests/test.py << TEST
lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
rm blocks/0 blocks/1
echo "--- Basic mounting ---" echo "--- Basic mounting ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
@@ -18,32 +26,22 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Invalid superblocks ---" echo "--- Invalid mount ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT; lfs_format(&lfs, &cfg) => 0;
TEST TEST
rm blocks/0 blocks/1 rm blocks/0 blocks/1
echo "--- Invalid mount ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT; lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST TEST
echo "--- Expanding superblock ---" echo "--- Valid corrupt mount ---"
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < 100; i++) {
lfs_mkdir(&lfs, "dummy") => 0;
lfs_remove(&lfs, "dummy") => 0;
}
lfs_unmount(&lfs) => 0;
TEST TEST
rm blocks/0
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dummy") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST

View File

@@ -59,7 +59,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hello", "c/hello") => 0; lfs_rename(&lfs, "b/hello", "c/hello") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/corrupt.py -n 1 rm -v blocks/7
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0; lfs_dir_open(&lfs, &dir[0], "b") => 0;
@@ -86,7 +86,8 @@ tests/test.py << TEST
lfs_rename(&lfs, "c/hello", "d/hello") => 0; lfs_rename(&lfs, "c/hello", "d/hello") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/corrupt.py -n 2 rm -v blocks/8
rm -v blocks/a
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0; lfs_dir_open(&lfs, &dir[0], "c") => 0;
@@ -107,32 +108,6 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Move file after corrupt ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
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;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 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) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir ---" echo "--- Move dir ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -165,7 +140,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hi", "c/hi") => 0; lfs_rename(&lfs, "b/hi", "c/hi") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/corrupt.py -n 1 rm -v blocks/7
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "b") => 0; lfs_dir_open(&lfs, &dir[0], "b") => 0;
@@ -181,6 +156,8 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0; strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
@@ -192,7 +169,8 @@ tests/test.py << TEST
lfs_rename(&lfs, "c/hi", "d/hi") => 0; lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
tests/corrupt.py -n 2 rm -v blocks/9
rm -v blocks/a
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0; lfs_dir_open(&lfs, &dir[0], "c") => 0;
@@ -201,6 +179,8 @@ tests/test.py << TEST
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0; strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0; lfs_dir_close(&lfs, &dir[0]) => 0;
@@ -209,36 +189,6 @@ tests/test.py << TEST
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 0; strcmp(info.name, "..") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move dir after corrupt ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
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;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "..") => 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) => 1;
strcmp(info.name, "hello") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0; lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
@@ -249,9 +199,9 @@ tests/test.py << TEST
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_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], "b/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c/hi") => LFS_ERR_NOENT; lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hi") => 0; lfs_dir_open(&lfs, &dir[0], "c/hi") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, ".") => 0; strcmp(info.name, ".") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1; lfs_dir_read(&lfs, &dir[0], &info) => 1;
@@ -267,55 +217,9 @@ tests/test.py << TEST
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_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], "b/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c/hello") => LFS_ERR_NOENT; lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0; 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;
lfs_file_read(&lfs, &file[0], buffer, 8) => 8;
memcmp(buffer, "bonjour\n", 8) => 0;
lfs_file_read(&lfs, &file[0], buffer, 6) => 6;
memcmp(buffer, "ohayo\n", 6) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Move state stealing ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "b") => 0;
lfs_remove(&lfs, "c") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "d/hi") => 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) => 1;
strcmp(info.name, "hola") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "bonjour") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 1;
strcmp(info.name, "ohayo") => 0;
lfs_dir_read(&lfs, &dir[0], &info) => 0;
lfs_dir_close(&lfs, &dir[0]) => 0;
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "b") => LFS_ERR_NOENT;
lfs_dir_open(&lfs, &dir[0], "c") => LFS_ERR_NOENT;
lfs_file_open(&lfs, &file[0], "d/hello", LFS_O_RDONLY) => 0;
lfs_file_read(&lfs, &file[0], buffer, 5) => 5; lfs_file_read(&lfs, &file[0], buffer, 5) => 5;
memcmp(buffer, "hola\n", 5) => 0; memcmp(buffer, "hola\n", 5) => 0;
lfs_file_read(&lfs, &file[0], buffer, 8) => 8; lfs_file_read(&lfs, &file[0], buffer, 8) => 8;

View File

@@ -15,29 +15,25 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "parent/child") => 0; lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0; lfs_remove(&lfs, "parent/orphan") => 0;
TEST 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 # linked-list entry and should orphan the child
tests/corrupt.py rm -v blocks/8
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &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);
lfs_deorphan(&lfs) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT; lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_ssize_t before = lfs_fs_size(&lfs); unsigned after = 0;
before => 8; lfs_traverse(&lfs, test_count, &after) => 0;
test_log("after", after);
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_ssize_t orphaned = lfs_fs_size(&lfs);
orphaned => 8;
lfs_mkdir(&lfs, "parent/otherchild") => 0;
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
lfs_ssize_t deorphaned = lfs_fs_size(&lfs);
deorphaned => 8;
int diff = before - after;
diff => 2;
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST

View File

@@ -139,23 +139,5 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Max path test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
memset(buffer, 'w', LFS_NAME_MAX+1);
buffer[LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG;
memcpy(buffer, "coffee/", strlen("coffee/"));
memset(buffer+strlen("coffee/"), 'w', LFS_NAME_MAX+1);
buffer[strlen("coffee/")+LFS_NAME_MAX+2] = '\0';
lfs_mkdir(&lfs, (char*)buffer) => LFS_ERR_NAMETOOLONG;
lfs_file_open(&lfs, &file[0], (char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT) => LFS_ERR_NAMETOOLONG;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---" echo "--- Results ---"
tests/stats.py tests/stats.py

View File

@@ -301,7 +301,7 @@ tests/test.py << TEST
size = strlen("hedgehoghog"); size = strlen("hedgehoghog");
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
lfs_soff_t off = offsets[i]; lfs_soff_t off = offsets[i];
memcpy(buffer, "hedgehoghog", size); memcpy(buffer, "hedgehoghog", size);
lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off; lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;

View File

@@ -23,14 +23,14 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i); sprintf((char*)buffer, "hairyhead%d", i);
lfs_file_open(&lfs, &file[0], (const char*)buffer, lfs_file_open(&lfs, &file[0], (const char*)buffer,
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0;
strcpy((char*)buffer, "hair"); strcpy((char*)buffer, "hair");
size = strlen((char*)buffer); size = strlen((char*)buffer);
for (int j = 0; j < startsizes[i]; j += size) { for (lfs_off_t j = 0; j < startsizes[i]; j += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size; lfs_file_write(&lfs, &file[0], buffer, size) => size;
} }
lfs_file_size(&lfs, &file[0]) => startsizes[i]; lfs_file_size(&lfs, &file[0]) => startsizes[i];
@@ -55,13 +55,13 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i); sprintf((char*)buffer, "hairyhead%d", i);
lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0;
lfs_file_size(&lfs, &file[0]) => hotsizes[i]; lfs_file_size(&lfs, &file[0]) => hotsizes[i];
size = strlen("hair"); size = strlen("hair");
int j = 0; lfs_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i]; j += size) { for (; j < startsizes[i] && j < hotsizes[i]; j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size; lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "hair", size) => 0; memcmp(buffer, "hair", size) => 0;
@@ -87,13 +87,13 @@ tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { for (unsigned i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) {
sprintf((char*)buffer, "hairyhead%d", i); sprintf((char*)buffer, "hairyhead%d", i);
lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0;
lfs_file_size(&lfs, &file[0]) => coldsizes[i]; lfs_file_size(&lfs, &file[0]) => coldsizes[i];
size = strlen("hair"); size = strlen("hair");
int j = 0; lfs_off_t j = 0;
for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i];
j += size) { j += size) {
lfs_file_read(&lfs, &file[0], buffer, size) => size; lfs_file_read(&lfs, &file[0], buffer, size) => size;