mirror of
https://github.com/littlefs-project/littlefs.git
synced 2025-11-16 12:34:34 +00:00
Compare commits
49 Commits
fix-seek-o
...
v2.10.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ed63b27be | ||
|
|
a666730044 | ||
|
|
47e738b788 | ||
|
|
81b0db0cdc | ||
|
|
63ab1ffb65 | ||
|
|
ca1081e7c4 | ||
|
|
76027f1502 | ||
|
|
61a1b0b496 | ||
|
|
ffafb9cbb1 | ||
|
|
5281a20f6c | ||
|
|
f55520380d | ||
|
|
936919d134 | ||
|
|
d2c3a47627 | ||
|
|
0320e7db0e | ||
|
|
caba4f31df | ||
|
|
152d03043c | ||
|
|
8d01895b32 | ||
|
|
0494ce7169 | ||
|
|
366100b140 | ||
|
|
630a0d87c2 | ||
|
|
3d0386489b | ||
|
|
b8e4433b34 | ||
|
|
dae656aa53 | ||
|
|
469c863c18 | ||
|
|
215613e41f | ||
|
|
2fcecc8894 | ||
|
|
78f9a5fcd3 | ||
|
|
83fe41b605 | ||
|
|
d7a911923b | ||
|
|
2ba4280a5e | ||
|
|
c961e1fe66 | ||
|
|
bd01a4c0ee | ||
|
|
999ef6656f | ||
|
|
b735c8fd7f | ||
|
|
30947054d4 | ||
|
|
80ca1ea300 | ||
|
|
815f0d85a5 | ||
|
|
dc92dec6d3 | ||
|
|
a6035071be | ||
|
|
232e736aae | ||
|
|
0de0389c6f | ||
|
|
1407db9556 | ||
|
|
ea431bd6ae | ||
|
|
2d62d2f4c9 | ||
|
|
1f82c0f27f | ||
|
|
a2c2e49e6b | ||
|
|
4a845be0be | ||
|
|
e1636d05ab | ||
|
|
ac207586ba |
24
.github/workflows/test.yml
vendored
24
.github/workflows/test.yml
vendored
@@ -273,7 +273,6 @@ jobs:
|
||||
}' | tee status/$(basename $f .csv).json
|
||||
done
|
||||
- name: upload-status-sizes
|
||||
if: ${{matrix.arch == 'x86_64'}}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: status-sizes-${{matrix.arch}}
|
||||
@@ -375,6 +374,29 @@ jobs:
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -DLFS_NO_INTRINSICS" make test
|
||||
|
||||
# run with all trace options enabled to at least make sure these
|
||||
# all compile
|
||||
test-yes-trace:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
# need a few things
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq gcc python3 python3-pip
|
||||
pip3 install toml
|
||||
gcc --version
|
||||
python3 --version
|
||||
- name: test-yes-trace
|
||||
run: |
|
||||
CFLAGS="$CFLAGS \
|
||||
-DLFS_YES_TRACE \
|
||||
-DLFS_RAMBD_YES_TRACE \
|
||||
-DLFS_FILEBD_YES_TRACE \
|
||||
-DLFS_RAMBD_YES_TRACE" \
|
||||
make test
|
||||
|
||||
# run LFS_MULTIVERSION tests
|
||||
test-multiversion:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
49
README.md
49
README.md
@@ -199,6 +199,47 @@ The tests assume a Linux environment and can be started with make:
|
||||
make test
|
||||
```
|
||||
|
||||
Tests are implemented in C in the .toml files found in the `tests` directory.
|
||||
When developing a feature or fixing a bug, it is frequently useful to run a
|
||||
single test case or suite of tests:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py -l runners/test_runner # list available test suites
|
||||
./scripts/test.py -L runners/test_runner test_dirs # list available test cases
|
||||
./scripts/test.py runners/test_runner test_dirs # run a specific test suite
|
||||
```
|
||||
|
||||
If an assert fails in a test, test.py will try to print information about the
|
||||
failure:
|
||||
|
||||
``` bash
|
||||
tests/test_dirs.toml:1:failure: test_dirs_root:1g12gg2 (PROG_SIZE=16, ERASE_SIZE=512) failed
|
||||
tests/test_dirs.toml:5:assert: assert failed with 0, expected eq 42
|
||||
lfs_mount(&lfs, cfg) => 42;
|
||||
```
|
||||
|
||||
This includes the test id, which can be passed to test.py to run only that
|
||||
specific test permutation:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 # run a specific test permutation
|
||||
./scripts/test.py runners/test_runner test_dirs_root:1g12gg2 --gdb # drop into gdb on failure
|
||||
```
|
||||
|
||||
Some other flags that may be useful:
|
||||
|
||||
```bash
|
||||
./scripts/test.py runners/test_runner -b -j # run tests in parallel
|
||||
./scripts/test.py runners/test_runner -v -O- # redirect stdout to stdout
|
||||
./scripts/test.py runners/test_runner -ddisk # capture resulting disk image
|
||||
```
|
||||
|
||||
See `-h/--help` for a full list of available flags:
|
||||
|
||||
``` bash
|
||||
./scripts/test.py --help
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The littlefs is provided under the [BSD-3-Clause] license. See
|
||||
@@ -251,6 +292,12 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
filesystem over USB. Allows mounting littlefs on a host PC without additional
|
||||
drivers.
|
||||
|
||||
- [ramcrc32bd] - An example block device using littlefs's 32-bit CRC for
|
||||
error-correction.
|
||||
|
||||
- [ramrsbd] - An example block device using Reed-Solomon codes for
|
||||
error-correction.
|
||||
|
||||
- [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed
|
||||
which already has block device drivers for most forms of embedded storage.
|
||||
littlefs is available in Mbed OS as the [LittleFileSystem] class.
|
||||
@@ -281,6 +328,8 @@ License Identifiers that are here available: http://spdx.org/licenses/
|
||||
[mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src
|
||||
[mklittlefs]: https://github.com/earlephilhower/mklittlefs
|
||||
[pico-littlefs-usb]: https://github.com/oyama/pico-littlefs-usb
|
||||
[ramcrc32bd]: https://github.com/geky/ramcrc32bd
|
||||
[ramrsbd]: https://github.com/geky/ramrsbd
|
||||
[Mbed OS]: https://github.com/armmbed/mbed-os
|
||||
[LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html
|
||||
[SPIFFS]: https://github.com/pellepl/spiffs
|
||||
|
||||
@@ -133,7 +133,7 @@ int lfs_filebd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
|
||||
int lfs_filebd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
LFS_FILEBD_TRACE("lfs_filebd_erase(%p, 0x%"PRIx32" (%"PRIu32"))",
|
||||
(void*)cfg, block, ((lfs_file_t*)cfg->context)->cfg->erase_size);
|
||||
(void*)cfg, block, ((lfs_filebd_t*)cfg->context)->cfg->erase_size);
|
||||
lfs_filebd_t *bd = cfg->context;
|
||||
|
||||
// check if erase is valid
|
||||
|
||||
266
lfs.c
266
lfs.c
@@ -282,6 +282,21 @@ static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
|
||||
|
||||
|
||||
/// Small type-level utilities ///
|
||||
|
||||
// some operations on paths
|
||||
static inline lfs_size_t lfs_path_namelen(const char *path) {
|
||||
return strcspn(path, "/");
|
||||
}
|
||||
|
||||
static inline bool lfs_path_islast(const char *path) {
|
||||
lfs_size_t namelen = lfs_path_namelen(path);
|
||||
return path[namelen + strspn(path + namelen, "/")] == '\0';
|
||||
}
|
||||
|
||||
static inline bool lfs_path_isdir(const char *path) {
|
||||
return path[lfs_path_namelen(path)] != '\0';
|
||||
}
|
||||
|
||||
// operations on block pairs
|
||||
static inline void lfs_pair_swap(lfs_block_t pair[2]) {
|
||||
lfs_block_t t = pair[0];
|
||||
@@ -389,18 +404,15 @@ struct lfs_diskoff {
|
||||
|
||||
// operations on global state
|
||||
static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
|
||||
}
|
||||
a->tag ^= b->tag;
|
||||
a->pair[0] ^= b->pair[0];
|
||||
a->pair[1] ^= b->pair[1];
|
||||
}
|
||||
|
||||
static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (((uint32_t*)a)[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return a->tag == 0
|
||||
&& a->pair[0] == 0
|
||||
&& a->pair[1] == 0;
|
||||
}
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
@@ -1461,32 +1473,46 @@ static int lfs_dir_find_match(void *data,
|
||||
return LFS_CMP_EQ;
|
||||
}
|
||||
|
||||
// lfs_dir_find tries to set path and id even if file is not found
|
||||
//
|
||||
// returns:
|
||||
// - 0 if file is found
|
||||
// - LFS_ERR_NOENT if file or parent is not found
|
||||
// - LFS_ERR_NOTDIR if parent is not a dir
|
||||
static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
|
||||
const char **path, uint16_t *id) {
|
||||
// we reduce path to a single name if we can find it
|
||||
const char *name = *path;
|
||||
if (id) {
|
||||
*id = 0x3ff;
|
||||
}
|
||||
|
||||
// default to root dir
|
||||
lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0);
|
||||
dir->tail[0] = lfs->root[0];
|
||||
dir->tail[1] = lfs->root[1];
|
||||
|
||||
// empty paths are not allowed
|
||||
if (*name == '\0') {
|
||||
return LFS_ERR_INVAL;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
nextname:
|
||||
// skip slashes
|
||||
name += strspn(name, "/");
|
||||
// skip slashes if we're a directory
|
||||
if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
|
||||
name += strspn(name, "/");
|
||||
}
|
||||
lfs_size_t namelen = strcspn(name, "/");
|
||||
|
||||
// skip '.' and root '..'
|
||||
if ((namelen == 1 && memcmp(name, ".", 1) == 0) ||
|
||||
(namelen == 2 && memcmp(name, "..", 2) == 0)) {
|
||||
// skip '.'
|
||||
if (namelen == 1 && memcmp(name, ".", 1) == 0) {
|
||||
name += namelen;
|
||||
goto nextname;
|
||||
}
|
||||
|
||||
// error on unmatched '..', trying to go above root?
|
||||
if (namelen == 2 && memcmp(name, "..", 2) == 0) {
|
||||
return LFS_ERR_INVAL;
|
||||
}
|
||||
|
||||
// skip if matched by '..' in name
|
||||
const char *suffix = name + namelen;
|
||||
lfs_size_t sufflen;
|
||||
@@ -1498,7 +1524,9 @@ nextname:
|
||||
break;
|
||||
}
|
||||
|
||||
if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
|
||||
if (sufflen == 1 && memcmp(suffix, ".", 1) == 0) {
|
||||
// noop
|
||||
} else if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
|
||||
depth -= 1;
|
||||
if (depth == 0) {
|
||||
name = suffix + sufflen;
|
||||
@@ -1512,14 +1540,14 @@ nextname:
|
||||
}
|
||||
|
||||
// found path
|
||||
if (name[0] == '\0') {
|
||||
if (*name == '\0') {
|
||||
return tag;
|
||||
}
|
||||
|
||||
// update what we've found so far
|
||||
*path = name;
|
||||
|
||||
// only continue if we hit a directory
|
||||
// only continue if we're a directory
|
||||
if (lfs_tag_type3(tag) != LFS_TYPE_DIR) {
|
||||
return LFS_ERR_NOTDIR;
|
||||
}
|
||||
@@ -1539,8 +1567,7 @@ nextname:
|
||||
tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
|
||||
LFS_MKTAG(0x780, 0, 0),
|
||||
LFS_MKTAG(LFS_TYPE_NAME, 0, namelen),
|
||||
// are we last name?
|
||||
(strchr(name, '/') == NULL) ? id : NULL,
|
||||
id,
|
||||
lfs_dir_find_match, &(struct lfs_dir_find_match){
|
||||
lfs, name, namelen});
|
||||
if (tag < 0) {
|
||||
@@ -2128,13 +2155,14 @@ static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir,
|
||||
// And we cap at half a block to avoid degenerate cases with
|
||||
// nearly-full metadata blocks.
|
||||
//
|
||||
lfs_size_t metadata_max = (lfs->cfg->metadata_max)
|
||||
? lfs->cfg->metadata_max
|
||||
: lfs->cfg->block_size;
|
||||
if (end - split < 0xff
|
||||
&& size <= lfs_min(
|
||||
lfs->cfg->block_size - 40,
|
||||
metadata_max - 40,
|
||||
lfs_alignup(
|
||||
(lfs->cfg->metadata_max
|
||||
? lfs->cfg->metadata_max
|
||||
: lfs->cfg->block_size)/2,
|
||||
metadata_max/2,
|
||||
lfs->cfg->prog_size))) {
|
||||
break;
|
||||
}
|
||||
@@ -2338,7 +2366,8 @@ fixmlist:;
|
||||
if (d->m.pair != pair) {
|
||||
for (int i = 0; i < attrcount; i++) {
|
||||
if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||
d->id == lfs_tag_id(attrs[i].tag)) {
|
||||
d->id == lfs_tag_id(attrs[i].tag) &&
|
||||
d->type != LFS_TYPE_DIR) {
|
||||
d->m.pair[0] = LFS_BLOCK_NULL;
|
||||
d->m.pair[1] = LFS_BLOCK_NULL;
|
||||
} else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
|
||||
@@ -2527,7 +2556,7 @@ static int lfs_dir_orphaningcommit(lfs_t *lfs, lfs_mdir_t *dir,
|
||||
if (err != LFS_ERR_NOENT) {
|
||||
if (lfs_gstate_hasorphans(&lfs->gstate)) {
|
||||
// next step, clean up orphans
|
||||
err = lfs_fs_preporphans(lfs, -hasparent);
|
||||
err = lfs_fs_preporphans(lfs, -(int8_t)hasparent);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
@@ -2603,12 +2632,12 @@ static int lfs_mkdir_(lfs_t *lfs, const char *path) {
|
||||
cwd.next = lfs->mlist;
|
||||
uint16_t id;
|
||||
err = lfs_dir_find(lfs, &cwd.m, &path, &id);
|
||||
if (!(err == LFS_ERR_NOENT && id != 0x3ff)) {
|
||||
if (!(err == LFS_ERR_NOENT && lfs_path_islast(path))) {
|
||||
return (err < 0) ? err : LFS_ERR_EXIST;
|
||||
}
|
||||
|
||||
// check that name fits
|
||||
lfs_size_t nlen = strlen(path);
|
||||
lfs_size_t nlen = lfs_path_namelen(path);
|
||||
if (nlen > lfs->name_max) {
|
||||
return LFS_ERR_NAMETOOLONG;
|
||||
}
|
||||
@@ -3057,7 +3086,7 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file,
|
||||
|
||||
// allocate entry for file if it doesn't exist
|
||||
lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id);
|
||||
if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) {
|
||||
if (tag < 0 && !(tag == LFS_ERR_NOENT && lfs_path_islast(path))) {
|
||||
err = tag;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -3077,8 +3106,14 @@ static int lfs_file_opencfg_(lfs_t *lfs, lfs_file_t *file,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// don't allow trailing slashes
|
||||
if (lfs_path_isdir(path)) {
|
||||
err = LFS_ERR_NOTDIR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// check that name fits
|
||||
lfs_size_t nlen = strlen(path);
|
||||
lfs_size_t nlen = lfs_path_namelen(path);
|
||||
if (nlen > lfs->name_max) {
|
||||
err = LFS_ERR_NAMETOOLONG;
|
||||
goto cleanup;
|
||||
@@ -3688,13 +3723,8 @@ static lfs_soff_t lfs_file_seek_(lfs_t *lfs, lfs_file_t *file,
|
||||
|
||||
// if we're only reading and our new offset is still in the file's cache
|
||||
// we can avoid flushing and needing to reread the data
|
||||
if (
|
||||
#ifndef LFS_READONLY
|
||||
!(file->flags & LFS_F_WRITING)
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
) {
|
||||
if ((file->flags & LFS_F_READING)
|
||||
&& file->off != lfs->cfg->block_size) {
|
||||
int oindex = lfs_ctz_index(lfs, &(lfs_off_t){file->pos});
|
||||
lfs_off_t noff = npos;
|
||||
int nindex = lfs_ctz_index(lfs, &noff);
|
||||
@@ -3836,6 +3866,12 @@ static int lfs_stat_(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
||||
return (int)tag;
|
||||
}
|
||||
|
||||
// only allow trailing slashes on dirs
|
||||
if (strchr(path, '/') != NULL
|
||||
&& lfs_tag_type3(tag) != LFS_TYPE_DIR) {
|
||||
return LFS_ERR_NOTDIR;
|
||||
}
|
||||
|
||||
return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info);
|
||||
}
|
||||
|
||||
@@ -3938,7 +3974,7 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
|
||||
uint16_t newid;
|
||||
lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
|
||||
if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) &&
|
||||
!(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) {
|
||||
!(prevtag == LFS_ERR_NOENT && lfs_path_islast(newpath))) {
|
||||
return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
|
||||
}
|
||||
|
||||
@@ -3949,8 +3985,14 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
|
||||
struct lfs_mlist prevdir;
|
||||
prevdir.next = lfs->mlist;
|
||||
if (prevtag == LFS_ERR_NOENT) {
|
||||
// if we're a file, don't allow trailing slashes
|
||||
if (lfs_path_isdir(newpath)
|
||||
&& lfs_tag_type3(oldtag) != LFS_TYPE_DIR) {
|
||||
return LFS_ERR_NOTDIR;
|
||||
}
|
||||
|
||||
// check that name fits
|
||||
lfs_size_t nlen = strlen(newpath);
|
||||
lfs_size_t nlen = lfs_path_namelen(newpath);
|
||||
if (nlen > lfs->name_max) {
|
||||
return LFS_ERR_NAMETOOLONG;
|
||||
}
|
||||
@@ -4010,7 +4052,8 @@ static int lfs_rename_(lfs_t *lfs, const char *oldpath, const char *newpath) {
|
||||
{LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT,
|
||||
LFS_TYPE_DELETE, newid, 0), NULL},
|
||||
{LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL},
|
||||
{LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath},
|
||||
{LFS_MKTAG(lfs_tag_type3(oldtag),
|
||||
newid, lfs_path_namelen(newpath)), newpath},
|
||||
{LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd},
|
||||
{LFS_MKTAG_IF(samepair,
|
||||
LFS_TYPE_DELETE, newoldid, 0), NULL}));
|
||||
@@ -4167,6 +4210,14 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
// which littlefs currently does not support
|
||||
LFS_ASSERT((bool)0x80000000);
|
||||
|
||||
// check that the required io functions are provided
|
||||
LFS_ASSERT(lfs->cfg->read != NULL);
|
||||
#ifndef LFS_READONLY
|
||||
LFS_ASSERT(lfs->cfg->prog != NULL);
|
||||
LFS_ASSERT(lfs->cfg->erase != NULL);
|
||||
LFS_ASSERT(lfs->cfg->sync != NULL);
|
||||
#endif
|
||||
|
||||
// validate that the lfs-cfg sizes were initiated properly before
|
||||
// performing any arithmetic logics with them
|
||||
LFS_ASSERT(lfs->cfg->read_size != 0);
|
||||
@@ -4203,6 +4254,15 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
LFS_ASSERT(lfs->cfg->compact_thresh == (lfs_size_t)-1
|
||||
|| lfs->cfg->compact_thresh <= lfs->cfg->block_size);
|
||||
|
||||
// check that metadata_max is a multiple of read_size and prog_size,
|
||||
// and a factor of the block_size
|
||||
LFS_ASSERT(!lfs->cfg->metadata_max
|
||||
|| lfs->cfg->metadata_max % lfs->cfg->read_size == 0);
|
||||
LFS_ASSERT(!lfs->cfg->metadata_max
|
||||
|| lfs->cfg->metadata_max % lfs->cfg->prog_size == 0);
|
||||
LFS_ASSERT(!lfs->cfg->metadata_max
|
||||
|| lfs->cfg->block_size % lfs->cfg->metadata_max == 0);
|
||||
|
||||
// setup read cache
|
||||
if (lfs->cfg->read_buffer) {
|
||||
lfs->rcache.buffer = lfs->cfg->read_buffer;
|
||||
@@ -4390,6 +4450,30 @@ cleanup:
|
||||
}
|
||||
#endif
|
||||
|
||||
struct lfs_tortoise_t {
|
||||
lfs_block_t pair[2];
|
||||
lfs_size_t i;
|
||||
lfs_size_t period;
|
||||
};
|
||||
|
||||
static int lfs_tortoise_detectcycles(
|
||||
const lfs_mdir_t *dir, struct lfs_tortoise_t *tortoise) {
|
||||
// detect cycles with Brent's algorithm
|
||||
if (lfs_pair_issync(dir->tail, tortoise->pair)) {
|
||||
LFS_WARN("Cycle detected in tail list");
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
if (tortoise->i == tortoise->period) {
|
||||
tortoise->pair[0] = dir->tail[0];
|
||||
tortoise->pair[1] = dir->tail[1];
|
||||
tortoise->i = 0;
|
||||
tortoise->period *= 2;
|
||||
}
|
||||
tortoise->i += 1;
|
||||
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
int err = lfs_init(lfs, cfg);
|
||||
if (err) {
|
||||
@@ -4398,23 +4482,16 @@ static int lfs_mount_(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
|
||||
// scan directory blocks for superblock and any global updates
|
||||
lfs_mdir_t dir = {.tail = {0, 1}};
|
||||
lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
|
||||
lfs_size_t tortoise_i = 1;
|
||||
lfs_size_t tortoise_period = 1;
|
||||
struct lfs_tortoise_t tortoise = {
|
||||
.pair = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
||||
.i = 1,
|
||||
.period = 1,
|
||||
};
|
||||
while (!lfs_pair_isnull(dir.tail)) {
|
||||
// detect cycles with Brent's algorithm
|
||||
if (lfs_pair_issync(dir.tail, tortoise)) {
|
||||
LFS_WARN("Cycle detected in tail list");
|
||||
err = LFS_ERR_CORRUPT;
|
||||
err = lfs_tortoise_detectcycles(&dir, &tortoise);
|
||||
if (err < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (tortoise_i == tortoise_period) {
|
||||
tortoise[0] = dir.tail[0];
|
||||
tortoise[1] = dir.tail[1];
|
||||
tortoise_i = 0;
|
||||
tortoise_period *= 2;
|
||||
}
|
||||
tortoise_i += 1;
|
||||
|
||||
// fetch next block in tail list
|
||||
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail,
|
||||
@@ -4627,22 +4704,17 @@ int lfs_fs_traverse_(lfs_t *lfs,
|
||||
}
|
||||
#endif
|
||||
|
||||
lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
|
||||
lfs_size_t tortoise_i = 1;
|
||||
lfs_size_t tortoise_period = 1;
|
||||
struct lfs_tortoise_t tortoise = {
|
||||
.pair = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
||||
.i = 1,
|
||||
.period = 1,
|
||||
};
|
||||
int err = LFS_ERR_OK;
|
||||
while (!lfs_pair_isnull(dir.tail)) {
|
||||
// detect cycles with Brent's algorithm
|
||||
if (lfs_pair_issync(dir.tail, tortoise)) {
|
||||
LFS_WARN("Cycle detected in tail list");
|
||||
err = lfs_tortoise_detectcycles(&dir, &tortoise);
|
||||
if (err < 0) {
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
if (tortoise_i == tortoise_period) {
|
||||
tortoise[0] = dir.tail[0];
|
||||
tortoise[1] = dir.tail[1];
|
||||
tortoise_i = 0;
|
||||
tortoise_period *= 2;
|
||||
}
|
||||
tortoise_i += 1;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int err = cb(data, dir.tail[i]);
|
||||
@@ -4721,22 +4793,17 @@ static int lfs_fs_pred(lfs_t *lfs,
|
||||
// iterate over all directory directory entries
|
||||
pdir->tail[0] = 0;
|
||||
pdir->tail[1] = 1;
|
||||
lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
|
||||
lfs_size_t tortoise_i = 1;
|
||||
lfs_size_t tortoise_period = 1;
|
||||
struct lfs_tortoise_t tortoise = {
|
||||
.pair = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
||||
.i = 1,
|
||||
.period = 1,
|
||||
};
|
||||
int err = LFS_ERR_OK;
|
||||
while (!lfs_pair_isnull(pdir->tail)) {
|
||||
// detect cycles with Brent's algorithm
|
||||
if (lfs_pair_issync(pdir->tail, tortoise)) {
|
||||
LFS_WARN("Cycle detected in tail list");
|
||||
err = lfs_tortoise_detectcycles(pdir, &tortoise);
|
||||
if (err < 0) {
|
||||
return LFS_ERR_CORRUPT;
|
||||
}
|
||||
if (tortoise_i == tortoise_period) {
|
||||
tortoise[0] = pdir->tail[0];
|
||||
tortoise[1] = pdir->tail[1];
|
||||
tortoise_i = 0;
|
||||
tortoise_period *= 2;
|
||||
}
|
||||
tortoise_i += 1;
|
||||
|
||||
if (lfs_pair_cmp(pdir->tail, pair) == 0) {
|
||||
return 0;
|
||||
@@ -4786,22 +4853,17 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
|
||||
// use fetchmatch with callback to find pairs
|
||||
parent->tail[0] = 0;
|
||||
parent->tail[1] = 1;
|
||||
lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
|
||||
lfs_size_t tortoise_i = 1;
|
||||
lfs_size_t tortoise_period = 1;
|
||||
struct lfs_tortoise_t tortoise = {
|
||||
.pair = {LFS_BLOCK_NULL, LFS_BLOCK_NULL},
|
||||
.i = 1,
|
||||
.period = 1,
|
||||
};
|
||||
int err = LFS_ERR_OK;
|
||||
while (!lfs_pair_isnull(parent->tail)) {
|
||||
// detect cycles with Brent's algorithm
|
||||
if (lfs_pair_issync(parent->tail, tortoise)) {
|
||||
LFS_WARN("Cycle detected in tail list");
|
||||
return LFS_ERR_CORRUPT;
|
||||
err = lfs_tortoise_detectcycles(parent, &tortoise);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
if (tortoise_i == tortoise_period) {
|
||||
tortoise[0] = parent->tail[0];
|
||||
tortoise[1] = parent->tail[1];
|
||||
tortoise_i = 0;
|
||||
tortoise_period *= 2;
|
||||
}
|
||||
tortoise_i += 1;
|
||||
|
||||
lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
|
||||
LFS_MKTAG(0x7ff, 0, 0x3ff),
|
||||
@@ -5884,7 +5946,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32", "
|
||||
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
|
||||
".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
|
||||
".lookahead_size=%"PRIu32", .read_buffer=%p, "
|
||||
".prog_buffer=%p, .lookahead_buffer=%p, "
|
||||
".name_max=%"PRIu32", .file_max=%"PRIu32", "
|
||||
@@ -5914,7 +5976,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32", "
|
||||
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
|
||||
".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
|
||||
".lookahead_size=%"PRIu32", .read_buffer=%p, "
|
||||
".prog_buffer=%p, .lookahead_buffer=%p, "
|
||||
".name_max=%"PRIu32", .file_max=%"PRIu32", "
|
||||
@@ -6051,7 +6113,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) {
|
||||
return err;
|
||||
}
|
||||
LFS_TRACE("lfs_file_open(%p, %p, \"%s\", %x)",
|
||||
(void*)lfs, (void*)file, path, flags);
|
||||
(void*)lfs, (void*)file, path, (unsigned)flags);
|
||||
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
|
||||
|
||||
err = lfs_file_open_(lfs, file, path, flags);
|
||||
@@ -6071,7 +6133,7 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
|
||||
}
|
||||
LFS_TRACE("lfs_file_opencfg(%p, %p, \"%s\", %x, %p {"
|
||||
".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})",
|
||||
(void*)lfs, (void*)file, path, flags,
|
||||
(void*)lfs, (void*)file, path, (unsigned)flags,
|
||||
(void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count);
|
||||
LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
|
||||
|
||||
@@ -6224,7 +6286,7 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
|
||||
|
||||
lfs_soff_t res = lfs_file_size_(lfs, file);
|
||||
|
||||
LFS_TRACE("lfs_file_size -> %"PRId32, res);
|
||||
LFS_TRACE("lfs_file_size -> %"PRIu32, res);
|
||||
LFS_UNLOCK(lfs->cfg);
|
||||
return res;
|
||||
}
|
||||
@@ -6433,7 +6495,7 @@ int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
|
||||
".read=%p, .prog=%p, .erase=%p, .sync=%p, "
|
||||
".read_size=%"PRIu32", .prog_size=%"PRIu32", "
|
||||
".block_size=%"PRIu32", .block_count=%"PRIu32", "
|
||||
".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
|
||||
".block_cycles=%"PRId32", .cache_size=%"PRIu32", "
|
||||
".lookahead_size=%"PRIu32", .read_buffer=%p, "
|
||||
".prog_buffer=%p, .lookahead_buffer=%p, "
|
||||
".name_max=%"PRIu32", .file_max=%"PRIu32", "
|
||||
|
||||
2
lfs.h
2
lfs.h
@@ -21,7 +21,7 @@ extern "C"
|
||||
// Software library version
|
||||
// Major (top-nibble), incremented on backwards incompatible changes
|
||||
// Minor (bottom-nibble), incremented on feature additions
|
||||
#define LFS_VERSION 0x00020009
|
||||
#define LFS_VERSION 0x0002000a
|
||||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
|
||||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))
|
||||
|
||||
|
||||
22
lfs_util.h
22
lfs_util.h
@@ -8,6 +8,9 @@
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
||||
#define LFS_STRINGIZE2(x) #x
|
||||
|
||||
// Users can override lfs_util.h with their own configuration by defining
|
||||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
||||
//
|
||||
@@ -15,11 +18,26 @@
|
||||
// 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
|
||||
#include LFS_STRINGIZE(LFS_CONFIG)
|
||||
#else
|
||||
|
||||
// Alternatively, users can provide a header file which defines
|
||||
// macros and other things consumed by littlefs.
|
||||
//
|
||||
// For example, provide my_defines.h, which contains
|
||||
// something like:
|
||||
//
|
||||
// #include <stddef.h>
|
||||
// extern void *my_malloc(size_t sz);
|
||||
// #define LFS_MALLOC(sz) my_malloc(sz)
|
||||
//
|
||||
// And build littlefs with the header by defining LFS_DEFINES.
|
||||
// (-DLFS_DEFINES=my_defines.h)
|
||||
|
||||
#ifdef LFS_DEFINES
|
||||
#include LFS_STRINGIZE(LFS_DEFINES)
|
||||
#endif
|
||||
|
||||
// System includes
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -1322,6 +1322,7 @@ void perm_run(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
};
|
||||
|
||||
|
||||
@@ -96,12 +96,13 @@ intmax_t bench_define(size_t define);
|
||||
#define CACHE_SIZE_i 6
|
||||
#define LOOKAHEAD_SIZE_i 7
|
||||
#define COMPACT_THRESH_i 8
|
||||
#define INLINE_MAX_i 9
|
||||
#define BLOCK_CYCLES_i 10
|
||||
#define ERASE_VALUE_i 11
|
||||
#define ERASE_CYCLES_i 12
|
||||
#define BADBLOCK_BEHAVIOR_i 13
|
||||
#define POWERLOSS_BEHAVIOR_i 14
|
||||
#define METADATA_MAX_i 9
|
||||
#define INLINE_MAX_i 10
|
||||
#define BLOCK_CYCLES_i 11
|
||||
#define ERASE_VALUE_i 12
|
||||
#define ERASE_CYCLES_i 13
|
||||
#define BADBLOCK_BEHAVIOR_i 14
|
||||
#define POWERLOSS_BEHAVIOR_i 15
|
||||
|
||||
#define READ_SIZE bench_define(READ_SIZE_i)
|
||||
#define PROG_SIZE bench_define(PROG_SIZE_i)
|
||||
@@ -112,6 +113,7 @@ intmax_t bench_define(size_t define);
|
||||
#define CACHE_SIZE bench_define(CACHE_SIZE_i)
|
||||
#define LOOKAHEAD_SIZE bench_define(LOOKAHEAD_SIZE_i)
|
||||
#define COMPACT_THRESH bench_define(COMPACT_THRESH_i)
|
||||
#define METADATA_MAX bench_define(METADATA_MAX_i)
|
||||
#define INLINE_MAX bench_define(INLINE_MAX_i)
|
||||
#define BLOCK_CYCLES bench_define(BLOCK_CYCLES_i)
|
||||
#define ERASE_VALUE bench_define(ERASE_VALUE_i)
|
||||
@@ -129,6 +131,7 @@ intmax_t bench_define(size_t define);
|
||||
BENCH_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
BENCH_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
BENCH_DEF(COMPACT_THRESH, 0) \
|
||||
BENCH_DEF(METADATA_MAX, 0) \
|
||||
BENCH_DEF(INLINE_MAX, 0) \
|
||||
BENCH_DEF(BLOCK_CYCLES, -1) \
|
||||
BENCH_DEF(ERASE_VALUE, 0xff) \
|
||||
@@ -137,7 +140,7 @@ intmax_t bench_define(size_t define);
|
||||
BENCH_DEF(POWERLOSS_BEHAVIOR, LFS_EMUBD_POWERLOSS_NOOP)
|
||||
|
||||
#define BENCH_GEOMETRY_DEFINE_COUNT 4
|
||||
#define BENCH_IMPLICIT_DEFINE_COUNT 15
|
||||
#define BENCH_IMPLICIT_DEFINE_COUNT 16
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1347,6 +1347,7 @@ static void run_powerloss_none(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
@@ -1425,6 +1426,7 @@ static void run_powerloss_linear(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
@@ -1520,6 +1522,7 @@ static void run_powerloss_log(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
@@ -1613,6 +1616,7 @@ static void run_powerloss_cycles(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
@@ -1804,6 +1808,7 @@ static void run_powerloss_exhaustive(
|
||||
.cache_size = CACHE_SIZE,
|
||||
.lookahead_size = LOOKAHEAD_SIZE,
|
||||
.compact_thresh = COMPACT_THRESH,
|
||||
.metadata_max = METADATA_MAX,
|
||||
.inline_max = INLINE_MAX,
|
||||
#ifdef LFS_MULTIVERSION
|
||||
.disk_version = DISK_VERSION,
|
||||
|
||||
@@ -89,13 +89,14 @@ intmax_t test_define(size_t define);
|
||||
#define CACHE_SIZE_i 6
|
||||
#define LOOKAHEAD_SIZE_i 7
|
||||
#define COMPACT_THRESH_i 8
|
||||
#define INLINE_MAX_i 9
|
||||
#define BLOCK_CYCLES_i 10
|
||||
#define ERASE_VALUE_i 11
|
||||
#define ERASE_CYCLES_i 12
|
||||
#define BADBLOCK_BEHAVIOR_i 13
|
||||
#define POWERLOSS_BEHAVIOR_i 14
|
||||
#define DISK_VERSION_i 15
|
||||
#define METADATA_MAX_i 9
|
||||
#define INLINE_MAX_i 10
|
||||
#define BLOCK_CYCLES_i 11
|
||||
#define ERASE_VALUE_i 12
|
||||
#define ERASE_CYCLES_i 13
|
||||
#define BADBLOCK_BEHAVIOR_i 14
|
||||
#define POWERLOSS_BEHAVIOR_i 15
|
||||
#define DISK_VERSION_i 16
|
||||
|
||||
#define READ_SIZE TEST_DEFINE(READ_SIZE_i)
|
||||
#define PROG_SIZE TEST_DEFINE(PROG_SIZE_i)
|
||||
@@ -106,6 +107,7 @@ intmax_t test_define(size_t define);
|
||||
#define CACHE_SIZE TEST_DEFINE(CACHE_SIZE_i)
|
||||
#define LOOKAHEAD_SIZE TEST_DEFINE(LOOKAHEAD_SIZE_i)
|
||||
#define COMPACT_THRESH TEST_DEFINE(COMPACT_THRESH_i)
|
||||
#define METADATA_MAX TEST_DEFINE(METADATA_MAX_i)
|
||||
#define INLINE_MAX TEST_DEFINE(INLINE_MAX_i)
|
||||
#define BLOCK_CYCLES TEST_DEFINE(BLOCK_CYCLES_i)
|
||||
#define ERASE_VALUE TEST_DEFINE(ERASE_VALUE_i)
|
||||
@@ -124,6 +126,7 @@ intmax_t test_define(size_t define);
|
||||
TEST_DEF(CACHE_SIZE, lfs_max(64,lfs_max(READ_SIZE,PROG_SIZE))) \
|
||||
TEST_DEF(LOOKAHEAD_SIZE, 16) \
|
||||
TEST_DEF(COMPACT_THRESH, 0) \
|
||||
TEST_DEF(METADATA_MAX, 0) \
|
||||
TEST_DEF(INLINE_MAX, 0) \
|
||||
TEST_DEF(BLOCK_CYCLES, -1) \
|
||||
TEST_DEF(ERASE_VALUE, 0xff) \
|
||||
@@ -133,7 +136,7 @@ intmax_t test_define(size_t define);
|
||||
TEST_DEF(DISK_VERSION, 0)
|
||||
|
||||
#define TEST_GEOMETRY_DEFINE_COUNT 4
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 16
|
||||
#define TEST_IMPLICIT_DEFINE_COUNT 17
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,10 +35,10 @@ LEXEMES = {
|
||||
'assert': ['assert'],
|
||||
'arrow': ['=>'],
|
||||
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
|
||||
'paren': ['\(', '\)'],
|
||||
'paren': [r'\(', r'\)'],
|
||||
'cmp': CMP.keys(),
|
||||
'logic': ['\&\&', '\|\|'],
|
||||
'sep': [':', ';', '\{', '\}', ','],
|
||||
'logic': [r'\&\&', r'\|\|'],
|
||||
'sep': [':', ';', r'\{', r'\}', ','],
|
||||
'op': ['->'], # specifically ops that conflict with cmp
|
||||
}
|
||||
|
||||
@@ -86,6 +86,13 @@ def write_header(f, limit=LIMIT):
|
||||
f.writeln("}")
|
||||
f.writeln()
|
||||
f.writeln("__attribute__((unused))")
|
||||
f.writeln("static void __pretty_assert_print_ptr(")
|
||||
f.writeln(" const void *v, size_t size) {")
|
||||
f.writeln(" (void)size;")
|
||||
f.writeln(" printf(\"%p\", v);")
|
||||
f.writeln("}")
|
||||
f.writeln()
|
||||
f.writeln("__attribute__((unused))")
|
||||
f.writeln("static void __pretty_assert_print_mem(")
|
||||
f.writeln(" const void *v, size_t size) {")
|
||||
f.writeln(" const uint8_t *v_ = v;")
|
||||
@@ -183,6 +190,23 @@ def write_header(f, limit=LIMIT):
|
||||
f.writeln(" _rh, strlen(_rh)); \\")
|
||||
f.writeln(" } \\")
|
||||
f.writeln("} while (0)")
|
||||
for op, cmp in sorted(CMP.items()):
|
||||
# Only EQ and NE are supported when compared to NULL.
|
||||
if cmp not in ['eq', 'ne']:
|
||||
continue
|
||||
f.writeln("#define __PRETTY_ASSERT_PTR_%s(lh, rh) do { \\"
|
||||
% cmp.upper())
|
||||
f.writeln(" const void *_lh = (const void*)(uintptr_t)lh; \\")
|
||||
f.writeln(" const void *_rh = (const void*)(uintptr_t)rh; \\")
|
||||
f.writeln(" if (!(_lh %s _rh)) { \\" % op)
|
||||
f.writeln(" __pretty_assert_fail( \\")
|
||||
f.writeln(" __FILE__, __LINE__, \\")
|
||||
f.writeln(" __pretty_assert_print_ptr, \"%s\", \\"
|
||||
% cmp)
|
||||
f.writeln(" (const void*){_lh}, 0, \\")
|
||||
f.writeln(" (const void*){_rh}, 0); \\")
|
||||
f.writeln(" } \\")
|
||||
f.writeln("} while (0)")
|
||||
f.writeln()
|
||||
f.writeln()
|
||||
|
||||
@@ -301,6 +325,8 @@ def p_assert(p):
|
||||
cmp = p.expect('cmp') ; p.accept('ws')
|
||||
rh = p_expr(p) ; p.accept('ws')
|
||||
p.expect(')')
|
||||
if rh == 'NULL' or lh == 'NULL':
|
||||
return mkassert('ptr', CMP[cmp], lh, rh)
|
||||
return mkassert('int', CMP[cmp], lh, rh)
|
||||
except ParseFailure:
|
||||
p.pop(state)
|
||||
|
||||
@@ -102,9 +102,9 @@ class TestCase:
|
||||
# the runner itself.
|
||||
for v_ in csplit(v):
|
||||
m = re.search(r'\brange\b\s*\('
|
||||
'(?P<start>[^,\s]*)'
|
||||
'\s*(?:,\s*(?P<stop>[^,\s]*)'
|
||||
'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
|
||||
r'(?P<start>[^,\s]*)'
|
||||
r'\s*(?:,\s*(?P<stop>[^,\s]*)'
|
||||
r'\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)',
|
||||
v_)
|
||||
if m:
|
||||
start = (int(m.group('start'), 0)
|
||||
@@ -163,8 +163,8 @@ class TestSuite:
|
||||
code_linenos = []
|
||||
for i, line in enumerate(f):
|
||||
match = re.match(
|
||||
'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
|
||||
'|' '(?P<code>code\s*=)',
|
||||
r'(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])'
|
||||
r'|' r'(?P<code>code\s*=)',
|
||||
line)
|
||||
if match and match.group('case'):
|
||||
case_linenos.append((i+1, match.group('name')))
|
||||
@@ -602,9 +602,9 @@ def find_perms(runner_, ids=[], **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<flags>[^\s]+)'
|
||||
'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<flags>[^\s]+)'
|
||||
r'\s+(?P<filtered>\d+)/(?P<perms>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -632,8 +632,8 @@ def find_perms(runner_, ids=[], **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -676,8 +676,8 @@ def find_path(runner_, id, **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
pattern = re.compile(
|
||||
'^(?P<case>[^\s]+)'
|
||||
'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
r'^(?P<case>[^\s]+)'
|
||||
r'\s+(?P<path>[^:]+):(?P<lineno>\d+)')
|
||||
# skip the first line
|
||||
for line in it.islice(proc.stdout, 1, None):
|
||||
m = pattern.match(line)
|
||||
@@ -706,7 +706,7 @@ def find_defines(runner_, id, **args):
|
||||
errors='replace',
|
||||
close_fds=False)
|
||||
defines = co.OrderedDict()
|
||||
pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)')
|
||||
pattern = re.compile(r'^(?P<define>\w+)=(?P<value>.+)')
|
||||
for line in proc.stdout:
|
||||
m = pattern.match(line)
|
||||
if m:
|
||||
@@ -781,12 +781,12 @@ def run_stage(name, runner_, ids, stdout_, trace_, output_, **args):
|
||||
failures = []
|
||||
killed = False
|
||||
|
||||
pattern = re.compile('^(?:'
|
||||
'(?P<op>running|finished|skipped|powerloss) '
|
||||
'(?P<id>(?P<case>[^:]+)[^\s]*)'
|
||||
'|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
|
||||
' *(?P<message>.*)'
|
||||
')$')
|
||||
pattern = re.compile(r'^(?:'
|
||||
r'(?P<op>running|finished|skipped|powerloss) '
|
||||
r'(?P<id>(?P<case>[^:]+)[^\s]*)'
|
||||
r'|' r'(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):'
|
||||
r' *(?P<message>.*)'
|
||||
r')$')
|
||||
locals = th.local()
|
||||
children = set()
|
||||
|
||||
|
||||
@@ -725,6 +725,82 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
[cases.test_dirs_remove_read]
|
||||
defines.N = 10
|
||||
if = 'N < BLOCK_COUNT/2'
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_mkdir(&lfs, "prickly-pear") => 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "prickly-pear/cactus%03d", i);
|
||||
lfs_mkdir(&lfs, path) => 0;
|
||||
}
|
||||
lfs_dir_t dir;
|
||||
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
|
||||
struct lfs_info info;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
for (int i = 0; i < N; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
lfs_unmount(&lfs);
|
||||
|
||||
for (lfs_size_t k = 0; k < N; k++) {
|
||||
for (lfs_size_t j = 0; j < N; j++) {
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir, "prickly-pear") => 0;
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, ".") == 0);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, "..") == 0);
|
||||
// iterate over dirs < j
|
||||
for (unsigned i = 0; i < j; i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
|
||||
// remove k while iterating
|
||||
char path[1024];
|
||||
sprintf(path, "prickly-pear/cactus%03d", k);
|
||||
lfs_remove(&lfs, path) => 0;
|
||||
|
||||
// iterate over dirs >= j
|
||||
for (unsigned i = j; i < ((k >= j) ? N-1 : N); i++) {
|
||||
char path[1024];
|
||||
sprintf(path, "cactus%03d", (k >= j && i >= k) ? i+1 : i);
|
||||
lfs_dir_read(&lfs, &dir, &info) => 1;
|
||||
assert(info.type == LFS_TYPE_DIR);
|
||||
assert(strcmp(info.name, path) == 0);
|
||||
}
|
||||
lfs_dir_read(&lfs, &dir, &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir) => 0;
|
||||
|
||||
// recreate k
|
||||
sprintf(path, "prickly-pear/cactus%03d", k);
|
||||
lfs_mkdir(&lfs, path) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
[cases.test_dirs_other_errors]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -137,6 +137,130 @@ code = '''
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# boundary seek and reads
|
||||
[cases.test_seek_boundary_read]
|
||||
defines.COUNT = 132
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_file_t file;
|
||||
lfs_file_open(&lfs, &file, "kitty",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
size_t size = strlen("kittycatcat");
|
||||
uint8_t buffer[1024];
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < COUNT; j++) {
|
||||
lfs_file_write(&lfs, &file, buffer, size);
|
||||
}
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDONLY) => 0;
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
const lfs_soff_t offsets[] = {
|
||||
512,
|
||||
1024-4,
|
||||
512+1,
|
||||
1024-4+1,
|
||||
512-1,
|
||||
1024-4-1,
|
||||
|
||||
512-strlen("kittycatcat"),
|
||||
1024-4-strlen("kittycatcat"),
|
||||
512-strlen("kittycatcat")+1,
|
||||
1024-4-strlen("kittycatcat")+1,
|
||||
512-strlen("kittycatcat")-1,
|
||||
1024-4-strlen("kittycatcat")-1,
|
||||
|
||||
strlen("kittycatcat")*(COUNT-2)-1,
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
lfs_soff_t off = offsets[i];
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read after
|
||||
lfs_file_seek(&lfs, &file, off+strlen("kittycatcat")+1, LFS_SEEK_SET)
|
||||
=> off+strlen("kittycatcat")+1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read before
|
||||
lfs_file_seek(&lfs, &file, off-strlen("kittycatcat")-1, LFS_SEEK_SET)
|
||||
=> off-strlen("kittycatcat")-1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
|
||||
// read @ 0
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read after
|
||||
lfs_file_seek(&lfs, &file, off+strlen("kittycatcat")+1, LFS_SEEK_SET)
|
||||
=> off+strlen("kittycatcat")+1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read before
|
||||
lfs_file_seek(&lfs, &file, off-strlen("kittycatcat")-1, LFS_SEEK_SET)
|
||||
=> off-strlen("kittycatcat")-1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
|
||||
// sync
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
|
||||
// read @ 0
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[off % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read after
|
||||
lfs_file_seek(&lfs, &file, off+strlen("kittycatcat")+1, LFS_SEEK_SET)
|
||||
=> off+strlen("kittycatcat")+1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off+1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
// read before
|
||||
lfs_file_seek(&lfs, &file, off-strlen("kittycatcat")-1, LFS_SEEK_SET)
|
||||
=> off-strlen("kittycatcat")-1;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer,
|
||||
&"kittycatcatkittycatcat"[(off-1) % strlen("kittycatcat")],
|
||||
size) => 0;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# boundary seek and writes
|
||||
[cases.test_seek_boundary_write]
|
||||
defines.COUNT = 132
|
||||
@@ -160,31 +284,54 @@ code = '''
|
||||
lfs_file_open(&lfs, &file, "kitty", LFS_O_RDWR) => 0;
|
||||
|
||||
size = strlen("hedgehoghog");
|
||||
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019, 1441};
|
||||
const lfs_soff_t offsets[] = {
|
||||
512,
|
||||
1024-4,
|
||||
512+1,
|
||||
1024-4+1,
|
||||
512-1,
|
||||
1024-4-1,
|
||||
|
||||
512-strlen("kittycatcat"),
|
||||
1024-4-strlen("kittycatcat"),
|
||||
512-strlen("kittycatcat")+1,
|
||||
1024-4-strlen("kittycatcat")+1,
|
||||
512-strlen("kittycatcat")-1,
|
||||
1024-4-strlen("kittycatcat")-1,
|
||||
|
||||
strlen("kittycatcat")*(COUNT-2)-1,
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
lfs_soff_t off = offsets[i];
|
||||
// write @ offset
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_write(&lfs, &file, buffer, size) => size;
|
||||
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hedgehoghog", size) => 0;
|
||||
|
||||
// read @ 0
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hedgehoghog", size) => 0;
|
||||
|
||||
lfs_file_sync(&lfs, &file) => 0;
|
||||
|
||||
// read @ 0
|
||||
lfs_file_seek(&lfs, &file, 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
// read @ offset
|
||||
lfs_file_seek(&lfs, &file, off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file, buffer, size) => size;
|
||||
memcmp(buffer, "hedgehoghog", size) => 0;
|
||||
|
||||
@@ -523,3 +523,30 @@ code = '''
|
||||
assert(memcmp(buffer, "hello!", 6) == 0);
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
# test that metadata_max does not cause problems for superblock compaction
|
||||
[cases.test_superblocks_metadata_max]
|
||||
defines.METADATA_MAX = [
|
||||
'lfs_max(512, PROG_SIZE)',
|
||||
'lfs_max(BLOCK_SIZE/2, PROG_SIZE)',
|
||||
'BLOCK_SIZE'
|
||||
]
|
||||
defines.N = [10, 100, 1000]
|
||||
code = '''
|
||||
lfs_t lfs;
|
||||
lfs_format(&lfs, cfg) => 0;
|
||||
lfs_mount(&lfs, cfg) => 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
lfs_file_t file;
|
||||
char name[256];
|
||||
sprintf(name, "hello%03x", i);
|
||||
lfs_file_open(&lfs, &file, name,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0;
|
||||
lfs_file_close(&lfs, &file) => 0;
|
||||
struct lfs_info info;
|
||||
lfs_stat(&lfs, name, &info) => 0;
|
||||
assert(strcmp(info.name, name) == 0);
|
||||
assert(info.type == LFS_TYPE_REG);
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user