Compare commits

..

2 Commits

Author SHA1 Message Date
Christopher Haster
f6e3193051 Added file config structure and lfs_file_opencfg
The optional config structure options up the possibility of adding
file-level configuration in a backwards compatible manner.

Also bumped minor version to v1.5
2018-07-16 15:39:10 -05:00
Damien George
97711d78c4 Added possibility to open multiple files with LFS_NO_MALLOC enabled. 2018-07-16 15:37:45 -05:00
23 changed files with 1691 additions and 4400 deletions

View File

@@ -18,11 +18,10 @@ script:
- 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=1 -DLFS_PROG_SIZE=1"
- make test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512"
- make test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048"
- make clean test QUIET=1 CFLAGS+="-DLFS_INLINE_MAX=0"
- make clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
# compile and find the code size with the smallest configuration
@@ -103,7 +102,7 @@ jobs:
- NAME=littlefs-fuse
install:
- 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
- gcc --version
before_script:
@@ -135,44 +134,53 @@ jobs:
- STAGE=deploy
- NAME=deploy
script:
# Find version defined in lfs.h
# Update tag for 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
- LFS_VERSION_PATCH=$(curl -f -u "$GEKY_BOT_RELEASES"
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs
| jq 'map(.ref | match(
"refs/tags/v'"$LFS_VERSION_MAJOR"'\\.'"$LFS_VERSION_MINOR"'\\.(.*)$")
.captures[].string | tonumber + 1) | max // 0')
# We have our new version
- LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR.$LFS_VERSION_PATCH"
- echo "VERSION $LFS_VERSION"
- LFS_VERSION="v$LFS_VERSION_MAJOR.$LFS_VERSION_MINOR"
- echo "littlefs 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')
if [ "$TRAVIS_COMMIT" == "$CURRENT_COMMIT" ]
curl -u $GEKY_BOT_RELEASES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs \
-d "{
\"ref\": \"refs/tags/$LFS_VERSION\",
\"sha\": \"$TRAVIS_COMMIT\"
}"
- |
curl -f -u $GEKY_BOT_RELEASES -X PATCH \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/git/refs/tags/$LFS_VERSION \
-d "{
\"sha\": \"$TRAVIS_COMMIT\"
}"
# Create release notes from commits
- LFS_PREV_VERSION="v$LFS_VERSION_MAJOR.$(($LFS_VERSION_MINOR-1))"
- |
if [ $(git tag -l "$LFS_PREV_VERSION") ]
then
# Build release notes
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 -u $GEKY_BOT_RELEASES -X POST \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases \
-d "{
\"tag_name\": \"$LFS_VERSION\",
\"target_commitish\": \"$TRAVIS_COMMIT\",
\"name\": \"${LFS_VERSION%.0}\",
\"body\": $(jq -sR '.' <<< "$CHANGES")
\"name\": \"$LFS_VERSION\"
}"
RELEASE=$(
curl -f -u $GEKY_BOT_RELEASES \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION
)
CHANGES=$(
git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep
)
curl -f -u $GEKY_BOT_RELEASES -X PATCH \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/$(
jq -r '.id' <<< "$RELEASE"
) \
-d "$(
jq -s '{
"body": ((.[0] // "" | sub("(?<=\n)#+ Changes.*"; ""; "mi"))
+ "### Changes\n\n" + .[1])
}' <(jq '.body' <<< "$RELEASE") <(jq -sR '.' <<< "$CHANGES")
)"
fi
# Manage statuses

View File

@@ -22,3 +22,15 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
*Note*:
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/

View File

@@ -37,8 +37,7 @@ size: $(OBJ)
.SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate \
test_entries test_interspersed test_alloc test_paths test_attrs \
test_move test_orphan test_corrupt
test_interspersed test_alloc test_paths test_orphan test_move test_corrupt
@rm test.c
test_%: tests/test_%.sh

View File

@@ -146,19 +146,6 @@ The tests assume a Linux environment and can be started with make:
make test
```
## License
The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html)
license. See [LICENSE.md](LICENSE.md) for more information. Contributions to
this project are accepted under the same license.
Individual files contain the following tag instead of the full license text.
SPDX-License-Identifier: BSD-3-Clause
This enables machine processing of license information based on the SPDX
License Identifiers that are here available: http://spdx.org/licenses/
## Related projects
[Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) -

View File

@@ -19,40 +19,6 @@
#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
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
lfs_emubd_t *emu = cfg->context;
@@ -80,13 +46,13 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
}
// 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 {
return -errno;
}
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
@@ -95,25 +61,6 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
if (err) {
return -errno;
}
}
// Load history
snprintf(emu->child, LFS_NAME_MAX, ".history");
f = fopen(emu->path, "r");
if (!f) {
memset(&emu->history, 0, sizeof(emu->history));
} else {
size_t res = fread(&emu->history, sizeof(emu->history), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
err = fclose(f);
if (err) {
return -errno;
}
}
return 0;
}
@@ -214,13 +161,6 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
return -errno;
}
// update history and stats
if (block != emu->history.blocks[0]) {
memcpy(&emu->history.blocks[1], &emu->history.blocks[0],
sizeof(emu->history) - sizeof(emu->history.blocks[0]));
emu->history.blocks[0] = block;
}
emu->stats.prog_count += 1;
return 0;
}
@@ -266,15 +206,13 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
lfs_emubd_t *emu = cfg->context;
// Just write out info/stats for later lookup
snprintf(emu->child, LFS_NAME_MAX, ".config");
snprintf(emu->child, LFS_NAME_MAX, "config");
FILE *f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
lfs_emubd_fromle32(emu);
if (res < 1) {
return -errno;
}
@@ -284,33 +222,13 @@ int lfs_emubd_sync(const struct lfs_config *cfg) {
return -errno;
}
snprintf(emu->child, LFS_NAME_MAX, ".stats");
snprintf(emu->child, LFS_NAME_MAX, "stats");
f = fopen(emu->path, "w");
if (!f) {
return -errno;
}
lfs_emubd_tole32(emu);
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) {
return -errno;
}

View File

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

4066
lfs.c

File diff suppressed because it is too large Load Diff

333
lfs.h
View File

@@ -21,14 +21,14 @@ extern "C"
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00020000
#define LFS_VERSION 0x00010005
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
// Version of On-disk data structures
// Major (top-nibble), incremented on backwards incompatible changes
// 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_MINOR (0xffff & (LFS_DISK_VERSION >> 0))
@@ -44,27 +44,9 @@ typedef int32_t lfs_soff_t;
typedef uint32_t lfs_block_t;
// Maximum size of all attributes per file in bytes, may be redefined but a
// 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.
// Max name size in bytes
#ifndef LFS_NAME_MAX
#define LFS_NAME_MAX 0xff
#endif
// Maximum inline file size in bytes. Large inline files require a larger
// cache size, but if a file can be inline it does not need its own data
// block. Limited to <= LFS_ATTR_MAX and <= cache_size. Stored in superblock
// and must be respected by other littlefs drivers.
#ifndef LFS_INLINE_MAX
#define LFS_INLINE_MAX 0xfff
#define LFS_NAME_MAX 255
#endif
// Possible error codes, these are negative to allow
@@ -72,7 +54,7 @@ typedef uint32_t lfs_block_t;
enum lfs_error {
LFS_ERR_OK = 0, // No error
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_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
@@ -82,40 +64,13 @@ enum lfs_error {
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_NAMETOOLONG = -36, // File name too long
};
// File types
enum lfs_type {
// file types
LFS_TYPE_REG = 0x004,
LFS_TYPE_DIR = 0x005,
// internally used types
LFS_TYPE_USER = 0x100,
LFS_TYPE_SUPERBLOCK = 0x002,
LFS_TYPE_ROOT = 0x003,
LFS_TYPE_CHILD = 0x001,
LFS_TYPE_NAME = 0x000,
LFS_TYPE_DELETE = 0x060,
LFS_TYPE_STRUCT = 0x020,
LFS_TYPE_GLOBALS = 0x0e0,
LFS_TYPE_TAIL = 0x080,
LFS_TYPE_SOFTTAIL = 0x080,
LFS_TYPE_HARDTAIL = 0x081,
LFS_TYPE_CRC = 0x0a0,
LFS_TYPE_DIRSTRUCT = 0x020,
LFS_TYPE_INLINESTRUCT = 0x021,
LFS_TYPE_CTZSTRUCT = 0x022,
// internal chip sources
LFS_FROM_REGION = 0x000,
LFS_FROM_DISK = 0x200,
LFS_FROM_MOVE = 0x040,
LFS_FROM_COMPACT = 0x041,
LFS_FROM_SPLIT = 0x042,
LFS_FROM_ATTRS = 0x043,
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
};
// File open flags
@@ -130,11 +85,10 @@ enum lfs_open_flags {
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS_F_DIRTY = 0x010000, // File does not match storage
LFS_F_WRITING = 0x020000, // File has been written since last flush
LFS_F_READING = 0x040000, // File has been read since last flush
LFS_F_ERRED = 0x080000, // An error occured during write
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x80000, // An error occured during write
};
// File seek flags
@@ -172,34 +126,26 @@ struct lfs_config {
// are propogated to the user.
int (*sync)(const struct lfs_config *c);
// Minimum size of a block read. All read operations will be a
// multiple of this value.
// Minimum size of a block read. This determines the size of read buffers.
// This may be larger than the physical read size to improve performance
// by caching more of the block device.
lfs_size_t read_size;
// Minimum size of a block program. All program operations will be a
// multiple of this value.
// Minimum size of a block program. This determines the size of program
// buffers. This may be larger than the physical program size to improve
// performance by caching more of the block device.
// Must be a multiple of the read size.
lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block.
// Must be a multiple of the read, program, and cache sizes.
// Must be a multiple of the program size.
lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of 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
// 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
@@ -216,24 +162,16 @@ struct lfs_config {
// lookahead block.
void *lookahead_buffer;
// Optional upper limit on file attributes in bytes. No downside for larger
// attributes size but must be less than LFS_ATTR_MAX. Defaults to
// LFS_ATTR_MAX when zero.Stored in superblock and must be respected by
// other littlefs drivers.
lfs_size_t attr_max;
// Optional, statically allocated buffer for files. Must be program sized.
// If enabled, only one file may be opened at a time.
void *file_buffer;
};
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX when zero. Stored in
// superblock and must be respected by other littlefs drivers.
lfs_size_t name_max;
// 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;
// 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;
};
// File info structure
@@ -248,130 +186,77 @@ struct lfs_info {
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 ///
typedef struct lfs_mattr {
int32_t tag;
const void *buffer;
const struct lfs_mattr *next;
} lfs_mattr_t;
typedef struct lfs_entry {
lfs_off_t off;
struct lfs_disk_entry {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
union {
struct {
lfs_block_t head;
lfs_size_t size;
} file;
lfs_block_t dir[2];
} u;
} d;
} lfs_entry_t;
typedef struct lfs_cache {
lfs_block_t block;
lfs_off_t off;
lfs_size_t size;
uint8_t *buffer;
} lfs_cache_t;
typedef union lfs_global {
uint32_t u32[3];
struct {
lfs_block_t movepair[2];
uint16_t moveid;
uint8_t deorphaned;
} l;
struct {
lfs_block_t movepair[2];
uint16_t moveid;
uint8_t orphans;
} g;
} 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 {
struct lfs_file *next;
uint16_t id;
uint8_t type;
lfs_mdir_t m;
lfs_block_t pair[2];
lfs_off_t poff;
struct lfs_ctz {
lfs_block_t head;
lfs_size_t size;
} ctz;
const struct lfs_file_config *cfg;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
const struct lfs_file_config *cfg;
} 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 {
char magic[8];
lfs_off_t off;
struct lfs_disk_superblock {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
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;
char magic[8];
} d;
} lfs_superblock_t;
typedef struct lfs_free {
@@ -384,23 +269,17 @@ typedef struct lfs_free {
// The littlefs type
typedef struct lfs {
const struct lfs_config *cfg;
lfs_block_t root[2];
lfs_file_t *files;
lfs_dir_t *dirs;
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_block_t root[2];
lfs_mlist_t *mlist;
uint32_t seed;
lfs_global_t globals;
lfs_global_t locals;
lfs_free_t free;
const struct lfs_config *cfg;
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;
bool deorphaned;
} lfs_t;
@@ -453,31 +332,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
// Returns a negative error code on failure.
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 ///
@@ -612,15 +466,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);
/// Filesystem-level filesystem operations
// Finds the current size of the filesystem
//
// Note: Result is best effort. If files share COW structures, the returned
// size may be larger than the filesystem actually is.
//
// Returns the number of allocated blocks, or a negative error code on failure.
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
/// Miscellaneous littlefs specific operations ///
// Traverse through all blocks in use by the filesystem
//
@@ -629,7 +475,16 @@ lfs_ssize_t lfs_fs_size(lfs_t *lfs);
// blocks are in use or how much of the storage is available.
//
// 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);
// Prunes any recoverable errors that may have occured in the filesystem
//
// Not needed to be called by user unless an operation is interrupted
// but the filesystem is still mounted. This is already called on first
// allocation.
//
// Returns a negative error code on failure.
int lfs_deorphan(lfs_t *lfs);
#ifdef __cplusplus

View File

@@ -11,7 +11,7 @@
// 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] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
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;
for (size_t i = 0; i < size; i++) {
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf];
crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf];
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 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).
//
// 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
// and modifying as needed.
// provided by the config file. To start I would suggest copying lfs_util.h and
// modifying as needed.
#ifdef LFS_CONFIG
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x
@@ -23,7 +23,6 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#ifndef LFS_NO_MALLOC
#include <stdlib.h>
@@ -88,15 +87,6 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t 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
static inline uint32_t lfs_npw2(uint32_t a) {
#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);
}
// 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) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
@@ -160,34 +150,13 @@ static inline uint32_t lfs_fromle32(uint32_t a) {
#endif
}
// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t 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
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
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 = 0xffffffff
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,107 +0,0 @@
#!/usr/bin/env python2
import struct
import binascii
TYPES = {
(0x1ff, 0x004): 'reg',
(0x1ff, 0x005): 'dir',
(0x1ff, 0x002): 'superblock',
(0x1ff, 0x003): 'root',
(0x1ff, 0x001): 'child',
(0x1ff, 0x060): 'delete',
(0x1f0, 0x0e0): 'globals',
(0x1ff, 0x080): 'tail soft',
(0x1ff, 0x081): 'tail hard',
(0x1f0, 0x0a0): 'crc',
(0x1ff, 0x020): 'struct dir',
(0x1ff, 0x021): 'struct inline',
(0x1ff, 0x022): '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, struct.error):
pass
if not file:
print 'Bad metadata pair {%s}' % ', '.join(blocks)
return 1
print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True))
# go through each tag, print useful information
print "%-4s %-8s %-14s %3s %3s %s" % (
'off', 'tag', 'type', 'id', 'len', 'dump')
tag = 0xffffffff
off = 4
while True:
try:
data = file.read(4)
crc = binascii.crc32(data, crc)
ntag, = struct.unpack('<I', data)
except struct.error:
break
tag ^= ntag
off += 4
type = (tag & 0x7fc00000) >> 22
id = (tag & 0x003ff000) >> 12
size = (tag & 0x00000fff) >> 0
iscrc = (type & 0x1f0) == 0x0f0
data = file.read(size)
if iscrc:
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 iscrc 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 iscrc:
crc = 0
tag ^= (type & 1) << 31
return 0
if __name__ == "__main__":
import sys
sys.exit(main(*sys.argv[1:]))

View File

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

View File

@@ -66,7 +66,7 @@ uintmax_t test;
#endif
#ifndef LFS_PROG_SIZE
#define LFS_PROG_SIZE LFS_READ_SIZE
#define LFS_PROG_SIZE 16
#endif
#ifndef LFS_BLOCK_SIZE
@@ -77,14 +77,6 @@ uintmax_t test;
#define LFS_BLOCK_COUNT 1024
#endif
#ifndef LFS_BLOCK_CYCLES
#define LFS_BLOCK_CYCLES 1024
#endif
#ifndef LFS_CACHE_SIZE
#define LFS_CACHE_SIZE 64
#endif
#ifndef LFS_LOOKAHEAD
#define LFS_LOOKAHEAD 128
#endif
@@ -100,8 +92,6 @@ const struct lfs_config cfg = {{
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.cache_size = LFS_CACHE_SIZE,
.lookahead = LFS_LOOKAHEAD,
}};

View File

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

View File

@@ -194,98 +194,55 @@ tests/test.py << TEST
lfs_file_read(&lfs, &file[0], buffer, size) => size;
memcmp(buffer, "exhaustion", size) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Dir exhaustion test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
int err;
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++) {
for (lfs_size_t i = 0;
i < (cfg.block_count-6)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&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_CREAT);
for (int i = 0; i < count+1; i++) {
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Chained dir exhaustion test ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_remove(&lfs, "exhaustion") => 0;
// find out max file size
lfs_mkdir(&lfs, "exhaustiondir") => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
lfs_mkdir(&lfs, (char*)buffer) => 0;
}
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
int count = 0;
int err;
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++) {
for (lfs_size_t i = 0;
i < (cfg.block_count-24)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_sync(&lfs, &file[0]) => 0;
lfs_file_close(&lfs, &file[0]) => 0;
for (int i = 0; i < 9; i++) {
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
@@ -294,25 +251,19 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
// shorten file to try a second chained dir
while (true) {
err = lfs_mkdir(&lfs, "exhaustiondir");
if (err != LFS_ERR_NOSPC) {
break;
lfs_remove(&lfs, "exhaustion") => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-26)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_ssize_t filesize = lfs_file_size(&lfs, &file[0]);
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_unmount(&lfs) => 0;
lfs_mkdir(&lfs, "exhaustiondir") => 0;
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
TEST
echo "--- Split dir test ---"
@@ -323,38 +274,28 @@ TEST
tests/test.py << TEST
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;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, cfg.block_size) => cfg.block_size;
lfs_file_write(&lfs, &file[0], (void*)"hi", 2) => 2;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < (cfg.block_count-4)*(cfg.block_size-8);
i < (cfg.block_count-6)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// open hole
lfs_remove(&lfs, "bump") => 0;
lfs_mkdir(&lfs, "splitdir") => 0;
lfs_file_open(&lfs, &file[0], "splitdir/bump",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
for (lfs_size_t i = 0; i < cfg.block_size; i += 2) {
memcpy(&buffer[i], "hi", 2);
}
lfs_file_write(&lfs, &file[0], buffer, 2*cfg.block_size) => LFS_ERR_NOSPC;
lfs_file_write(&lfs, &file[0], buffer, size) => LFS_ERR_NOSPC;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
@@ -373,7 +314,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -384,7 +325,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -392,6 +333,7 @@ tests/test.py << TEST
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// rewrite one file
@@ -401,7 +343,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -415,7 +357,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -435,7 +377,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -446,7 +388,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
@@ -454,6 +396,7 @@ tests/test.py << TEST
// remount to force reset of lookahead
lfs_unmount(&lfs) => 0;
lfs_mount(&lfs, &cfg) => 0;
// rewrite one file with a hole of one block
@@ -463,7 +406,7 @@ tests/test.py << TEST
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
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) {
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 root attribute ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_setattr(&lfs, "/", 'A', "aaaa", 4) => 0;
lfs_setattr(&lfs, "/", 'B', "bbbbbb", 6) => 0;
lfs_setattr(&lfs, "/", 'C', "ccccc", 5) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_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_setattr(&lfs, "/", 'B', "", 0) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 0;
lfs_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_setattr(&lfs, "/", 'B', "dddddd", 6) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 6;
lfs_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_setattr(&lfs, "/", 'B', "eee", 3) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 3;
lfs_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_setattr(&lfs, "/", 'A', buffer, LFS_ATTR_MAX+1) => LFS_ERR_NOSPC;
lfs_setattr(&lfs, "/", 'B', "fffffffff", 9) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 6) => 9;
lfs_getattr(&lfs, "/", 'C', buffer+10, 5) => 5;
lfs_unmount(&lfs) => 0;
TEST
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_getattr(&lfs, "/", 'A', buffer, 4) => 4;
lfs_getattr(&lfs, "/", 'B', buffer+4, 9) => 9;
lfs_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
lfs_mktree
lfs_chktree
BLOCKS="$(ls blocks | grep -vw '[01]')"
echo "--- Block corruption ---"
for b in $BLOCKS
for i in {0..33}
do
rm -rf blocks
mkdir blocks
ln -s /dev/zero blocks/$b
ln -s /dev/zero blocks/$(printf '%x' $i)
lfs_mktree
lfs_chktree
done
echo "--- Block persistance ---"
for b in $BLOCKS
for i in {0..33}
do
rm -rf blocks
mkdir blocks
lfs_mktree
chmod a-w blocks/$b
chmod a-w blocks/$(printf '%x' $i)
lfs_mktree
lfs_chktree
done
@@ -97,7 +96,7 @@ done
echo "--- Big region corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..512}
for i in {2..255}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done
@@ -107,7 +106,7 @@ lfs_chktree
echo "--- Alternating corruption ---"
rm -rf blocks
mkdir blocks
for i in {2..1024..2}
for i in {2..511..2}
do
ln -s /dev/zero blocks/$(printf '%x' $i)
done

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;
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 ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
@@ -18,32 +26,22 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/zero blocks/0
ln -f -s /dev/zero blocks/1
echo "--- Invalid mount ---"
tests/test.py << TEST
lfs_format(&lfs, &cfg) => LFS_ERR_NOSPC;
lfs_format(&lfs, &cfg) => 0;
TEST
rm blocks/0 blocks/1
echo "--- Invalid mount ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
TEST
echo "--- Expanding superblock ---"
echo "--- Valid corrupt mount ---"
tests/test.py << TEST
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
rm blocks/0
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_mkdir(&lfs, "dummy") => 0;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -59,7 +59,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hello", "c/hello") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 1
rm -v blocks/7
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 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_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 2
rm -v blocks/8
rm -v blocks/a
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
lfs_dir_open(&lfs, &dir[0], "c") => 0;
@@ -107,32 +108,6 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
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 ---"
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
@@ -165,7 +140,7 @@ tests/test.py << TEST
lfs_rename(&lfs, "b/hi", "c/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 1
rm -v blocks/7
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 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;
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_unmount(&lfs) => 0;
@@ -192,7 +169,8 @@ tests/test.py << TEST
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
lfs_unmount(&lfs) => 0;
TEST
tests/corrupt.py -n 2
rm -v blocks/9
rm -v blocks/a
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 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;
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_close(&lfs, &dir[0]) => 0;
@@ -209,36 +189,6 @@ tests/test.py << TEST
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 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_unmount(&lfs) => 0;
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], "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;
strcmp(info.name, ".") => 0;
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], "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_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_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;

View File

@@ -15,29 +15,25 @@ tests/test.py << TEST
lfs_mkdir(&lfs, "parent/child") => 0;
lfs_remove(&lfs, "parent/orphan") => 0;
TEST
# corrupt most recent commit, this should be the update to the previous
# remove most recent file, this should be the update to the previous
# linked-list entry and should orphan the child
tests/corrupt.py
rm -v blocks/8
tests/test.py << TEST
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_ssize_t before = lfs_fs_size(&lfs);
before => 8;
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;
unsigned after = 0;
lfs_traverse(&lfs, test_count, &after) => 0;
test_log("after", after);
int diff = before - after;
diff => 2;
lfs_unmount(&lfs) => 0;
TEST

View File

@@ -139,23 +139,5 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0;
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 ---"
tests/stats.py