Generated v2 prefixes
This commit is contained in:
@@ -192,7 +192,7 @@ More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and
|
|||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
The littlefs comes with a test suite designed to run on a PC using the
|
The littlefs comes with a test suite designed to run on a PC using the
|
||||||
[emulated block device](bd/lfs2_testbd.h) found in the `bd` directory.
|
[emulated block device](bd/lfs2_emubd.h) found in the `bd` directory.
|
||||||
The tests assume a Linux environment and can be started with make:
|
The tests assume a Linux environment and can be started with make:
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ static int lfs2_bd_prog(lfs2_t *lfs2,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pcache must have been flushed, either by programming and
|
// pcache must have been flushed, either by programming an
|
||||||
// entire block or manually flushing the pcache
|
// entire block or manually flushing the pcache
|
||||||
LFS2_ASSERT(pcache->block == LFS2_BLOCK_NULL);
|
LFS2_ASSERT(pcache->block == LFS2_BLOCK_NULL);
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) {
|
|||||||
|
|
||||||
// some operations on paths
|
// some operations on paths
|
||||||
static inline lfs2_size_t lfs2_path_namelen(const char *path) {
|
static inline lfs2_size_t lfs2_path_namelen(const char *path) {
|
||||||
return strcspn(path, "/");
|
return (lfs2_size_t)strcspn(path, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool lfs2_path_islast(const char *path) {
|
static inline bool lfs2_path_islast(const char *path) {
|
||||||
@@ -1291,6 +1291,7 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2,
|
|||||||
|
|
||||||
// found a match for our fetcher?
|
// found a match for our fetcher?
|
||||||
if ((fmask & tag) == (fmask & ftag)) {
|
if ((fmask & tag) == (fmask & ftag)) {
|
||||||
|
LFS2_ASSERT(cb != NULL);
|
||||||
int res = cb(data, tag, &(struct lfs2_diskoff){
|
int res = cb(data, tag, &(struct lfs2_diskoff){
|
||||||
dir->pair[0], off+sizeof(tag)});
|
dir->pair[0], off+sizeof(tag)});
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
@@ -1501,7 +1502,7 @@ nextname:
|
|||||||
if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) {
|
if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) {
|
||||||
name += strspn(name, "/");
|
name += strspn(name, "/");
|
||||||
}
|
}
|
||||||
lfs2_size_t namelen = strcspn(name, "/");
|
lfs2_size_t namelen = (lfs2_size_t)strcspn(name, "/");
|
||||||
|
|
||||||
// skip '.'
|
// skip '.'
|
||||||
if (namelen == 1 && memcmp(name, ".", 1) == 0) {
|
if (namelen == 1 && memcmp(name, ".", 1) == 0) {
|
||||||
@@ -1520,7 +1521,7 @@ nextname:
|
|||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (true) {
|
while (true) {
|
||||||
suffix += strspn(suffix, "/");
|
suffix += strspn(suffix, "/");
|
||||||
sufflen = strcspn(suffix, "/");
|
sufflen = (lfs2_size_t)strcspn(suffix, "/");
|
||||||
if (sufflen == 0) {
|
if (sufflen == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1761,7 +1762,7 @@ static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) {
|
|||||||
|
|
||||||
commit->off = noff;
|
commit->off = noff;
|
||||||
// perturb valid bit?
|
// perturb valid bit?
|
||||||
commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24);
|
commit->ptag = ntag ^ ((lfs2_tag_t)(0x80 & ~eperturb) << 24);
|
||||||
// reset crc for next commit
|
// reset crc for next commit
|
||||||
commit->crc = 0xffffffff;
|
commit->crc = 0xffffffff;
|
||||||
|
|
||||||
@@ -3244,10 +3245,12 @@ static int lfs2_file_open_(lfs2_t *lfs2, lfs2_file_t *file,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file) {
|
static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file) {
|
||||||
#ifndef LFS2_READONLY
|
|
||||||
int err = lfs2_file_sync_(lfs2, file);
|
|
||||||
#else
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
#ifndef LFS2_READONLY
|
||||||
|
// it's not safe to do anything if our file errored
|
||||||
|
if (!(file->flags & LFS2_F_ERRED)) {
|
||||||
|
err = lfs2_file_sync_(lfs2, file);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// remove from list of mdirs
|
// remove from list of mdirs
|
||||||
@@ -3429,18 +3432,12 @@ relocate:
|
|||||||
|
|
||||||
#ifndef LFS2_READONLY
|
#ifndef LFS2_READONLY
|
||||||
static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) {
|
static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) {
|
||||||
if (file->flags & LFS2_F_ERRED) {
|
|
||||||
// it's not safe to do anything if our file errored
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int err = lfs2_file_flush(lfs2, file);
|
int err = lfs2_file_flush(lfs2, file);
|
||||||
if (err) {
|
if (err) {
|
||||||
file->flags |= LFS2_F_ERRED;
|
file->flags |= LFS2_F_ERRED;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((file->flags & LFS2_F_DIRTY) &&
|
if ((file->flags & LFS2_F_DIRTY) &&
|
||||||
!lfs2_pair_isnull(file->m.pair)) {
|
!lfs2_pair_isnull(file->m.pair)) {
|
||||||
// before we commit metadata, we need sync the disk to make sure
|
// before we commit metadata, we need sync the disk to make sure
|
||||||
@@ -3485,6 +3482,17 @@ static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) {
|
|||||||
file->flags &= ~LFS2_F_DIRTY;
|
file->flags &= ~LFS2_F_DIRTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mark any other file handles as dirty + desync
|
||||||
|
for (lfs2_file_t *f = (lfs2_file_t*)lfs2->mlist; f; f = f->next) {
|
||||||
|
if (file != f
|
||||||
|
&& f->type == LFS2_TYPE_REG
|
||||||
|
&& lfs2_pair_cmp(f->m.pair, file->m.pair) == 0
|
||||||
|
&& f->id == file->id) {
|
||||||
|
f->flags |= LFS2_F_DUSTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file->flags &= ~LFS2_F_ERRED & ~LFS2_F_DUSTY;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -3692,7 +3700,7 @@ static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file,
|
|||||||
return nsize;
|
return nsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->flags &= ~LFS2_F_ERRED;
|
file->flags &= ~LFS2_F_ERRED & ~LFS2_F_DUSTY;
|
||||||
return nsize;
|
return nsize;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -4771,7 +4779,8 @@ int lfs2_fs_traverse_(lfs2_t *lfs2,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((f->flags & LFS2_F_DIRTY) && !(f->flags & LFS2_F_INLINE)) {
|
if (((f->flags & LFS2_F_DIRTY) || (f->flags & LFS2_F_DUSTY))
|
||||||
|
&& !(f->flags & LFS2_F_INLINE)) {
|
||||||
int err = lfs2_ctz_traverse(lfs2, &f->cache, &lfs2->rcache,
|
int err = lfs2_ctz_traverse(lfs2, &f->cache, &lfs2->rcache,
|
||||||
f->ctz.head, f->ctz.size, cb, data);
|
f->ctz.head, f->ctz.size, cb, data);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -135,14 +135,15 @@ enum lfs2_open_flags {
|
|||||||
|
|
||||||
// internally used flags
|
// internally used flags
|
||||||
#ifndef LFS2_READONLY
|
#ifndef LFS2_READONLY
|
||||||
LFS2_F_DIRTY = 0x010000, // File does not match storage
|
LFS2_F_DIRTY = 0x00010000, // File does not match storage due to write
|
||||||
LFS2_F_WRITING = 0x020000, // File has been written since last flush
|
LFS2_F_DUSTY = 0x00020000, // File does not match storage due to desync
|
||||||
|
LFS2_F_WRITING = 0x00040000, // File has been written since last flush
|
||||||
#endif
|
#endif
|
||||||
LFS2_F_READING = 0x040000, // File has been read since last flush
|
LFS2_F_READING = 0x00080000, // File has been read since last flush
|
||||||
#ifndef LFS2_READONLY
|
#ifndef LFS2_READONLY
|
||||||
LFS2_F_ERRED = 0x080000, // An error occurred during write
|
LFS2_F_ERRED = 0x00100000, // An error occurred during write
|
||||||
#endif
|
#endif
|
||||||
LFS2_F_INLINE = 0x100000, // Currently inlined in directory entry
|
LFS2_F_INLINE = 0x01000000, // Currently inlined in directory entry
|
||||||
};
|
};
|
||||||
|
|
||||||
// File seek flags
|
// File seek flags
|
||||||
|
|||||||
@@ -250,6 +250,189 @@ code = '''
|
|||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# multiple handle allocation test
|
||||||
|
#
|
||||||
|
# this tests that multiple open handles to the same file don't clobber
|
||||||
|
# each other
|
||||||
|
[cases.test_alloc_multihandle]
|
||||||
|
defines.FILES = 2
|
||||||
|
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / FILES)'
|
||||||
|
defines.GC = [false, true]
|
||||||
|
defines.COMPACT_THRESH = ['-1', '0', 'BLOCK_SIZE/2']
|
||||||
|
defines.INFER_BC = [false, true]
|
||||||
|
defines.SYNC = [false, true]
|
||||||
|
code = '''
|
||||||
|
const char *names[] = {"eggs", "spinach"};
|
||||||
|
lfs2_file_t files[FILES];
|
||||||
|
|
||||||
|
lfs2_t lfs2;
|
||||||
|
lfs2_format(&lfs2, cfg) => 0;
|
||||||
|
struct lfs2_config cfg_ = *cfg;
|
||||||
|
if (INFER_BC) {
|
||||||
|
cfg_.block_count = 0;
|
||||||
|
}
|
||||||
|
lfs2_mount(&lfs2, &cfg_) => 0;
|
||||||
|
lfs2_mkdir(&lfs2, "breakfast") => 0;
|
||||||
|
|
||||||
|
// write one file
|
||||||
|
char path[1024];
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_open(&lfs2, &files[0], path,
|
||||||
|
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
|
||||||
|
if (GC) {
|
||||||
|
lfs2_fs_gc(&lfs2) => 0;
|
||||||
|
}
|
||||||
|
size_t size = strlen(names[0]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
lfs2_file_write(&lfs2, &files[0], names[0], size) => size;
|
||||||
|
}
|
||||||
|
// sync?
|
||||||
|
if (SYNC) {
|
||||||
|
lfs2_file_sync(&lfs2, &files[0]) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the other file
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_open(&lfs2, &files[1], path,
|
||||||
|
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
|
||||||
|
if (GC) {
|
||||||
|
lfs2_fs_gc(&lfs2) => 0;
|
||||||
|
}
|
||||||
|
size = strlen(names[1]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
lfs2_file_write(&lfs2, &files[1], names[1], size) => size;
|
||||||
|
}
|
||||||
|
// sync?
|
||||||
|
if (SYNC) {
|
||||||
|
lfs2_file_sync(&lfs2, &files[1]) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to read from both
|
||||||
|
for (int n = 0; n < FILES; n++) {
|
||||||
|
lfs2_file_rewind(&lfs2, &files[n]) => 0;
|
||||||
|
size_t size = strlen(names[n]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
lfs2_file_read(&lfs2, &files[n], buffer, size) => size;
|
||||||
|
assert(memcmp(buffer, names[n], size) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int n = 0; n < FILES; n++) {
|
||||||
|
lfs2_file_close(&lfs2, &files[n]) => 0;
|
||||||
|
}
|
||||||
|
lfs2_unmount(&lfs2) => 0;
|
||||||
|
|
||||||
|
// check after remounting
|
||||||
|
lfs2_mount(&lfs2, &cfg_) => 0;
|
||||||
|
{
|
||||||
|
// last one wins
|
||||||
|
int n = FILES-1;
|
||||||
|
char path[1024];
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_t file;
|
||||||
|
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
|
||||||
|
size_t size = strlen(names[n]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
lfs2_file_read(&lfs2, &file, buffer, size) => size;
|
||||||
|
assert(memcmp(buffer, names[n], size) == 0);
|
||||||
|
}
|
||||||
|
lfs2_file_close(&lfs2, &file) => 0;
|
||||||
|
}
|
||||||
|
lfs2_unmount(&lfs2) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
|
# multiple handle allocation reuse test
|
||||||
|
[cases.test_alloc_multihandle_reuse]
|
||||||
|
defines.FILES = 2
|
||||||
|
defines.SIZE = '(((BLOCK_SIZE-8)*(BLOCK_COUNT-6)) / (FILES+1))'
|
||||||
|
defines.CYCLES = [1, 10]
|
||||||
|
defines.INFER_BC = [false, true]
|
||||||
|
defines.SYNC = [false, true]
|
||||||
|
code = '''
|
||||||
|
const char *names[] = {"eggs", "spinach"};
|
||||||
|
lfs2_file_t files[FILES];
|
||||||
|
|
||||||
|
lfs2_t lfs2;
|
||||||
|
lfs2_format(&lfs2, cfg) => 0;
|
||||||
|
struct lfs2_config cfg_ = *cfg;
|
||||||
|
if (INFER_BC) {
|
||||||
|
cfg_.block_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs2_mount(&lfs2, &cfg_) => 0;
|
||||||
|
lfs2_mkdir(&lfs2, "breakfast") => 0;
|
||||||
|
|
||||||
|
// write one file
|
||||||
|
char path[1024];
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_open(&lfs2, &files[0], path,
|
||||||
|
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_EXCL) => 0;
|
||||||
|
if (GC) {
|
||||||
|
lfs2_fs_gc(&lfs2) => 0;
|
||||||
|
}
|
||||||
|
size_t size = strlen(names[0]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
lfs2_file_write(&lfs2, &files[0], names[0], size) => size;
|
||||||
|
}
|
||||||
|
// sync?
|
||||||
|
if (SYNC) {
|
||||||
|
lfs2_file_sync(&lfs2, &files[0]) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int c = 0; c < CYCLES; c++) {
|
||||||
|
// write the other file
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_open(&lfs2, &files[1], path,
|
||||||
|
LFS2_O_RDWR | LFS2_O_CREAT | LFS2_O_TRUNC) => 0;
|
||||||
|
if (GC) {
|
||||||
|
lfs2_fs_gc(&lfs2) => 0;
|
||||||
|
}
|
||||||
|
size = strlen(names[1]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
lfs2_file_write(&lfs2, &files[1], names[1], size) => size;
|
||||||
|
}
|
||||||
|
// sync?
|
||||||
|
if (SYNC) {
|
||||||
|
lfs2_file_sync(&lfs2, &files[1]) => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to read from both
|
||||||
|
for (int n = 0; n < FILES; n++) {
|
||||||
|
lfs2_file_rewind(&lfs2, &files[n]) => 0;
|
||||||
|
size_t size = strlen(names[n]);
|
||||||
|
for (lfs2_size_t i = 0; i < SIZE; i += size) {
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
lfs2_file_read(&lfs2, &files[n], buffer, size) => size;
|
||||||
|
assert(memcmp(buffer, names[n], size) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lfs2_file_close(&lfs2, &files[1]) => 0;
|
||||||
|
}
|
||||||
|
lfs2_file_close(&lfs2, &files[0]) => 0;
|
||||||
|
lfs2_unmount(&lfs2) => 0;
|
||||||
|
|
||||||
|
// check after remounting
|
||||||
|
lfs2_mount(&lfs2, &cfg_) => 0;
|
||||||
|
{
|
||||||
|
// last one wins
|
||||||
|
int n = (SYNC) ? FILES-1 : 0;
|
||||||
|
char path[1024];
|
||||||
|
sprintf(path, "breakfast/quiche");
|
||||||
|
lfs2_file_t file;
|
||||||
|
lfs2_file_open(&lfs2, &file, path, LFS2_O_RDONLY) => 0;
|
||||||
|
size_t size = strlen(names[n]);
|
||||||
|
for (int i = 0; i < SIZE; i += size) {
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
lfs2_file_read(&lfs2, &file, buffer, size) => size;
|
||||||
|
assert(memcmp(buffer, names[n], size) == 0);
|
||||||
|
}
|
||||||
|
lfs2_file_close(&lfs2, &file) => 0;
|
||||||
|
}
|
||||||
|
lfs2_unmount(&lfs2) => 0;
|
||||||
|
'''
|
||||||
|
|
||||||
# exhaustion test
|
# exhaustion test
|
||||||
[cases.test_alloc_exhaustion]
|
[cases.test_alloc_exhaustion]
|
||||||
defines.INFER_BC = [false, true]
|
defines.INFER_BC = [false, true]
|
||||||
|
|||||||
Reference in New Issue
Block a user