Compare commits

..

7 Commits

Author SHA1 Message Date
Christopher Haster
f935fc0be6 Fixed lookahead overflow and removed unbounded lookahead pointers
As pointed out by davidefer, the lookahead pointer modular arithmetic
does not work around integer overflow when the pointer size is not a
multiple of the block count.

To avoid overflow problems, the easy solution is to stop trying to
work around integer overflows and keep the lookahead offset inside the
block device. To make this work, the ack was modified into a resetable
counter that is decremented every block allocation.

As a plus, quite a bit of the allocation logic ended up simplified.
2018-04-10 15:14:27 -05:00
Christopher Haster
6a89ecba39 Added test for lookahead overflow 2018-04-09 14:37:35 -05:00
Christopher Haster
89a7630d84 Fixed issue with lookahead trusting old lookahead blocks
One of the big simplifications in littlefs's implementation is the
complete lack of tracking free blocks, allowing operations to simply
drop blocks that are no longer in use.

However, this means the lookahead buffer can easily contain outdated
blocks that were previously deleted. This is usually fine, as littlefs
will rescan the storage if it can't find a free block in the lookahead
buffer, but after changes that caused littlefs to more conservatively
respect the alloc acks (e611cf5), any scanned blocks after an ack would
be incorrectly trusted.

The fix is to eagerly scan ahead in the lookahead when we allocate so
that alloc acks are better able to discredit old lookahead blocks. Since
usually alloc acks are tightly coupled to allocations of one or two blocks,
this allows littlefs to properly rescan every set of allocations.

This may still be a concern if there is a long series of worn out
blocks, but in the worst case littlefs will conservatively avoid using
blocks it's not sure about.

Found by davidefer
2018-04-09 14:37:35 -05:00
Christopher Haster
43eac3083b Renamed test_parallel tests to test_interespersed
The name test_parallel gave off the incorrect impression that these
tests are multithreaded.
2018-04-08 17:31:09 -05:00
Christopher Haster
dbc3cb1798 Fixed Travis rate-limit issue with Github requests
Using credentials avoids rate-limiting based on Travis's IP address
2018-04-08 17:31:09 -05:00
Christopher Haster
93ece2e87a Removed outdated note about moves and powerloss 2018-04-08 17:31:05 -05:00
Christopher Haster
d9c076d909 Removed the uninitialized read for invalid superblocks 2018-03-19 00:39:40 -05:00
6 changed files with 348 additions and 332 deletions

View File

@@ -35,7 +35,7 @@ script:
if [ "$TRAVIS_TEST_RESULT" -eq 0 ] if [ "$TRAVIS_TEST_RESULT" -eq 0 ]
then then
CURR=$(tail -n1 sizes | awk '{print $1}') CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ PREV=$(curl -u $GEKY_BOT_STATUSES https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\") | jq -re "select(.sha != \"$TRAVIS_COMMIT\")
| .statuses[] | select(.context == \"$STAGE/$NAME\").description | .statuses[] | select(.context == \"$STAGE/$NAME\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \ | capture(\"code size is (?<size>[0-9]+)\").size" \
@@ -165,7 +165,8 @@ jobs:
\"name\": \"$LFS_VERSION\" \"name\": \"$LFS_VERSION\"
}" }"
RELEASE=$( RELEASE=$(
curl -f https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION curl -f -u $GEKY_BOT_RELEASES \
https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases/tags/$LFS_VERSION
) )
CHANGES=$( CHANGES=$(
git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep git log --oneline $LFS_PREV_VERSION.. --grep='^Merge' --invert-grep

View File

@@ -33,8 +33,8 @@ size: $(OBJ)
$(SIZE) -t $^ $(SIZE) -t $^
.SUFFIXES: .SUFFIXES:
test: test_format test_dirs test_files test_seek test_truncate test_parallel \ test: test_format test_dirs test_files test_seek test_truncate \
test_alloc test_paths test_orphan test_move test_corrupt test_interspersed test_alloc test_paths test_orphan test_move test_corrupt
test_%: tests/test_%.sh test_%: tests/test_%.sh
ifdef QUIET ifdef QUIET
@./$< | sed -n '/^[-=]/p' @./$< | sed -n '/^[-=]/p'

488
lfs.c
View File

@@ -270,8 +270,7 @@ int lfs_deorphan(lfs_t *lfs);
static int lfs_alloc_lookahead(void *p, lfs_block_t block) { static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
lfs_t *lfs = p; lfs_t *lfs = p;
lfs_block_t off = (((lfs_soff_t)(block - lfs->free.begin) lfs_block_t off = ((block - lfs->free.off)
% (lfs_soff_t)(lfs->cfg->block_count))
+ lfs->cfg->block_count) % lfs->cfg->block_count; + lfs->cfg->block_count) % lfs->cfg->block_count;
if (off < lfs->free.size) { if (off < lfs->free.size) {
@@ -283,27 +282,39 @@ static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
while (true) { while (true) {
while (lfs->free.off != lfs->free.size) { while (lfs->free.i != lfs->free.size) {
lfs_block_t off = lfs->free.off; lfs_block_t off = lfs->free.i;
lfs->free.off += 1; lfs->free.i += 1;
lfs->free.ack -= 1;
if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) {
// found a free block // found a free block
*block = (lfs->free.begin + off) % lfs->cfg->block_count; *block = (lfs->free.off + off) % lfs->cfg->block_count;
// eagerly find next off so an alloc ack can
// discredit old lookahead blocks
while (lfs->free.i != lfs->free.size &&
(lfs->free.buffer[lfs->free.i / 32]
& (1U << (lfs->free.i % 32)))) {
lfs->free.i += 1;
lfs->free.ack -= 1;
}
return 0; return 0;
} }
} }
// check if we have looked at all blocks since last ack // check if we have looked at all blocks since last ack
if (lfs->free.off == lfs->free.ack - lfs->free.begin) { //if (lfs->free.i == lfs->free.ack - lfs->free.off) {
LFS_WARN("No more free space %d", lfs->free.off + lfs->free.begin); if (lfs->free.ack == 0) {
LFS_WARN("No more free space %d", lfs->free.i + lfs->free.off);
return LFS_ERR_NOSPC; return LFS_ERR_NOSPC;
} }
lfs->free.begin += lfs->free.size; lfs->free.off = (lfs->free.off + lfs->free.size)
lfs->free.size = lfs_min(lfs->cfg->lookahead, % lfs->cfg->block_count;
lfs->free.ack - lfs->free.begin); lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->free.ack);
lfs->free.off = 0; lfs->free.i = 0;
// find mask of free blocks from tree // find mask of free blocks from tree
memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
@@ -315,7 +326,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
} }
static void lfs_alloc_ack(lfs_t *lfs) { static void lfs_alloc_ack(lfs_t *lfs) {
lfs->free.ack = lfs->free.off-1 + lfs->free.begin + lfs->cfg->block_count; lfs->free.ack = lfs->cfg->block_count;
} }
@@ -386,6 +397,10 @@ static inline bool lfs_pairsync(
(paira[0] == pairb[1] && paira[1] == pairb[0]); (paira[0] == pairb[1] && paira[1] == pairb[0]);
} }
static inline lfs_size_t lfs_entry_size(const lfs_entry_t *entry) {
return 4 + entry->d.elen + entry->d.alen + entry->d.nlen;
}
static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
// allocate pair of dir blocks // allocate pair of dir blocks
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@@ -469,93 +484,27 @@ static int lfs_dir_fetch(lfs_t *lfs,
return 0; return 0;
} }
struct lfs_commit {
uint32_t crc;
lfs_block_t block;
lfs_off_t off;
};
static int lfs_commit(lfs_t *lfs, struct lfs_commit *c,
const void *data, lfs_size_t size) {
lfs_crc(&c->crc, data, size);
int err = lfs_bd_prog(lfs, c->block, c->off, data, size);
c->off += size;
return err;
}
struct lfs_region { struct lfs_region {
lfs_off_t off; lfs_off_t oldoff;
lfs_ssize_t diff; lfs_size_t oldlen;
const void *newdata;
int (*commit)(lfs_t *lfs, struct lfs_commit *c, lfs_size_t newlen;
const void *data, lfs_size_t size);
const void *data;
lfs_size_t size;
struct lfs_region *next;
}; };
static int lfs_commit_mem(lfs_t *lfs, struct lfs_commit *c,
const void *data, lfs_size_t size) {
return lfs_commit(lfs, c, data, size);
}
struct lfs_commit_disk {
lfs_block_t block;
lfs_off_t off;
struct lfs_region *regions;
};
static int lfs_commit_disk(lfs_t *lfs, struct lfs_commit *c,
const void *p, lfs_size_t size) {
const struct lfs_commit_disk *d = p;
struct lfs_region *r = d->regions;
lfs_off_t off = 0;
while (true) {
if (r && r->off == off) {
lfs_off_t orig = c->off;
int err = r->commit(lfs, c, r->data, r->size);
if (err) {
return err;
}
off += (c->off - orig) - r->diff;
r = r->next;
} else if (off < size) {
uint8_t data;
int err = lfs_bd_read(lfs, d->block, d->off + off, &data, 1);
if (err) {
return err;
}
err = lfs_commit(lfs, c, &data, 1);
if (err) {
return err;
}
off += 1;
} else {
return 0;
}
}
}
static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
struct lfs_region *regions) { const struct lfs_region *regions, int count) {
// state for copying over
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
lfs_size_t oldsize = (0x7fffffff & dir->d.size) - 4;
bool relocated = false;
// increment revision count // increment revision count
dir->d.rev += 1; dir->d.rev += 1;
// keep pairs in order such that pair[0] is most recent // keep pairs in order such that pair[0] is most recent
lfs_pairswap(dir->pair); lfs_pairswap(dir->pair);
for (struct lfs_region *r = regions; r; r = r->next) { for (int i = 0; i < count; i++) {
dir->d.size += r->diff; dir->d.size += regions[i].newlen - regions[i].oldlen;
} }
const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]};
bool relocated = false;
while (true) { while (true) {
if (true) { if (true) {
int err = lfs_bd_erase(lfs, dir->pair[0]); int err = lfs_bd_erase(lfs, dir->pair[0]);
@@ -566,19 +515,10 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
struct lfs_commit c = { uint32_t crc = 0xffffffff;
.crc = 0xffffffff,
.block = dir->pair[0],
.off = 0,
};
lfs_dir_tole32(&dir->d); lfs_dir_tole32(&dir->d);
err = lfs_commit_disk(lfs, &c, &(struct lfs_commit_disk){ lfs_crc(&crc, &dir->d, sizeof(dir->d));
oldpair[1], 0, err = lfs_bd_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d));
&(struct lfs_region){
0, 0,
lfs_commit_mem, &dir->d, sizeof(dir->d),
regions}}, oldsize);
lfs_dir_fromle32(&dir->d); lfs_dir_fromle32(&dir->d);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
@@ -587,9 +527,48 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
c.crc = lfs_tole32(c.crc); int i = 0;
err = lfs_bd_prog(lfs, dir->pair[0], c.off, &c.crc, 4); lfs_off_t oldoff = sizeof(dir->d);
c.crc = lfs_fromle32(c.crc); lfs_off_t newoff = sizeof(dir->d);
while (newoff < (0x7fffffff & dir->d.size)-4) {
if (i < count && regions[i].oldoff == oldoff) {
lfs_crc(&crc, regions[i].newdata, regions[i].newlen);
err = lfs_bd_prog(lfs, dir->pair[0],
newoff, regions[i].newdata, regions[i].newlen);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
oldoff += regions[i].oldlen;
newoff += regions[i].newlen;
i += 1;
} else {
uint8_t data;
err = lfs_bd_read(lfs, oldpair[1], oldoff, &data, 1);
if (err) {
return err;
}
lfs_crc(&crc, &data, 1);
err = lfs_bd_prog(lfs, dir->pair[0], newoff, &data, 1);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
oldoff += 1;
newoff += 1;
}
}
crc = lfs_tole32(crc);
err = lfs_bd_prog(lfs, dir->pair[0], newoff, &crc, 4);
crc = lfs_fromle32(crc);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
@@ -613,13 +592,12 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
if (ncrc != c.crc) { if (ncrc != crc) {
goto relocate; goto relocate;
} }
} }
break; break;
relocate: relocate:
//commit was corrupted //commit was corrupted
LFS_DEBUG("Bad block at %d", dir->pair[0]); LFS_DEBUG("Bad block at %d", dir->pair[0]);
@@ -662,18 +640,29 @@ relocate:
return 0; return 0;
} }
static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, const void *data) {
lfs_entry_tole32(&entry->d);
int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)},
{entry->off+sizeof(entry->d), entry->d.nlen, data, entry->d.nlen}
}, data ? 2 : 1);
lfs_entry_fromle32(&entry->d);
return err;
}
static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, struct lfs_region *regions) { lfs_entry_t *entry, const void *data) {
// check if we fit, if top bit is set we do not and move on // check if we fit, if top bit is set we do not and move on
while (true) { while (true) {
if ((0x7fffffff & dir->d.size) + entry->size <= lfs->cfg->block_size) { if (dir->d.size + lfs_entry_size(entry) <= lfs->cfg->block_size) {
entry->off = dir->d.size - 4; entry->off = dir->d.size - 4;
for (struct lfs_region *r = regions; r; r = r->next) {
r->off += entry->off;
}
lfs_entry_tole32(&entry->d); lfs_entry_tole32(&entry->d);
int err = lfs_dir_commit(lfs, dir, regions); int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, 0, &entry->d, sizeof(entry->d)},
{entry->off, 0, data, entry->d.nlen}
}, 2);
lfs_entry_fromle32(&entry->d); lfs_entry_fromle32(&entry->d);
return err; return err;
} }
@@ -689,12 +678,11 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
dir->d.tail[0] = olddir.d.tail[0]; dir->d.tail[0] = olddir.d.tail[0];
dir->d.tail[1] = olddir.d.tail[1]; dir->d.tail[1] = olddir.d.tail[1];
entry->off = dir->d.size - 4; entry->off = dir->d.size - 4;
for (struct lfs_region *r = regions; r; r = r->next) {
r->off += entry->off;
}
lfs_entry_tole32(&entry->d); lfs_entry_tole32(&entry->d);
err = lfs_dir_commit(lfs, dir, regions); err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
{entry->off, 0, &entry->d, sizeof(entry->d)},
{entry->off, 0, data, entry->d.nlen}
}, 2);
lfs_entry_fromle32(&entry->d); lfs_entry_fromle32(&entry->d);
if (err) { if (err) {
return err; return err;
@@ -703,7 +691,7 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
olddir.d.size |= 0x80000000; olddir.d.size |= 0x80000000;
olddir.d.tail[0] = dir->pair[0]; olddir.d.tail[0] = dir->pair[0];
olddir.d.tail[1] = dir->pair[1]; olddir.d.tail[1] = dir->pair[1];
return lfs_dir_commit(lfs, &olddir, NULL); return lfs_dir_commit(lfs, &olddir, NULL, 0);
} }
int err = lfs_dir_fetch(lfs, dir, dir->d.tail); int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
@@ -713,10 +701,10 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
} }
} }
static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
const lfs_entry_t *entry) {
// check if we should just drop the directory block // check if we should just drop the directory block
if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4 + entry->size) { if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4
+ lfs_entry_size(entry)) {
lfs_dir_t pdir; lfs_dir_t pdir;
int res = lfs_pred(lfs, dir->pair, &pdir); int res = lfs_pred(lfs, dir->pair, &pdir);
if (res < 0) { if (res < 0) {
@@ -727,15 +715,14 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir,
pdir.d.size &= dir->d.size | 0x7fffffff; pdir.d.size &= dir->d.size | 0x7fffffff;
pdir.d.tail[0] = dir->d.tail[0]; pdir.d.tail[0] = dir->d.tail[0];
pdir.d.tail[1] = dir->d.tail[1]; pdir.d.tail[1] = dir->d.tail[1];
return lfs_dir_commit(lfs, &pdir, NULL); return lfs_dir_commit(lfs, &pdir, NULL, 0);
} }
} }
// shift out the entry // shift out the entry
int err = lfs_dir_commit(lfs, dir, int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
&(struct lfs_region){ {entry->off, lfs_entry_size(entry), NULL, 0},
entry->off, -entry->size, }, 1);
lfs_commit_mem, NULL, 0});
if (err) { if (err) {
return err; return err;
} }
@@ -747,7 +734,7 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir,
f->pair[0] = 0xffffffff; f->pair[0] = 0xffffffff;
f->pair[1] = 0xffffffff; f->pair[1] = 0xffffffff;
} else if (f->poff > entry->off) { } else if (f->poff > entry->off) {
f->poff -= entry->size; f->poff -= lfs_entry_size(entry);
} }
} }
} }
@@ -755,8 +742,8 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir,
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) { for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
if (lfs_paircmp(d->pair, dir->pair) == 0) { if (lfs_paircmp(d->pair, dir->pair) == 0) {
if (d->off > entry->off) { if (d->off > entry->off) {
d->off -= entry->size; d->off -= lfs_entry_size(entry);
d->pos -= entry->size; d->pos -= lfs_entry_size(entry);
} }
} }
} }
@@ -764,82 +751,6 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir,
return 0; return 0;
} }
static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, struct lfs_region *regions) {
lfs_ssize_t diff = 0;
for (struct lfs_region *r = regions; r; r = r->next) {
diff += r->diff;
}
// do we still fit?
if ((0x7fffffff & dir->d.size) + diff <= lfs->cfg->block_size) {
for (struct lfs_region *r = regions; r; r = r->next) {
r->off += entry->off;
}
lfs_entry_tole32(&entry->d);
int err = lfs_dir_commit(lfs, dir, regions);
lfs_entry_fromle32(&entry->d);
if (err) {
return err;
}
// shift over any files/directories that are affected
for (lfs_file_t *f = lfs->files; f; f = f->next) {
if (lfs_paircmp(f->pair, dir->pair) == 0) {
if (f->poff > entry->off) {
f->poff += diff;
}
}
}
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
if (lfs_paircmp(d->pair, dir->pair) == 0) {
if (d->off > entry->off) {
d->off += diff;
d->pos += diff;
}
}
}
} else {
lfs_dir_t olddir = *dir;
lfs_entry_t oldentry = {
.off = entry->off,
.size = entry->size - diff,
.d.type = entry->d.type | LFS_STRUCT_MOVED,
};
// mark as moving
int err = lfs_dir_commit(lfs, &olddir,
&(struct lfs_region){
oldentry.off, 0,
lfs_commit_mem, &oldentry.d.type, 1});
if (err) {
return err;
}
// append updated entry
lfs_entry_tole32(&entry->d);
err = lfs_dir_append(lfs, dir, entry,
&(struct lfs_region){
0, +entry->size,
lfs_commit_disk, &(struct lfs_commit_disk){
olddir.pair[0], entry->off, regions}, oldentry.size});
lfs_entry_fromle32(&entry->d);
if (err) {
return err;
}
// remove old entry
err = lfs_dir_remove(lfs, dir, &oldentry);
if (err) {
return err;
}
}
return 0;
}
static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) {
if (!(0x80000000 & dir->d.size)) { if (!(0x80000000 & dir->d.size)) {
@@ -864,9 +775,8 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
} }
entry->off = dir->off; entry->off = dir->off;
entry->size = 4 + entry->d.elen + entry->d.alen + entry->d.nlen; dir->off += lfs_entry_size(entry);
dir->off += entry->size; dir->pos += lfs_entry_size(entry);
dir->pos += entry->size;
return 0; return 0;
} }
@@ -884,7 +794,10 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
// special case for root dir // special case for root dir
if (pathname[0] == '\0') { if (pathname[0] == '\0') {
*entry = (lfs_entry_t){ *entry = (lfs_entry_t){
.d.type = LFS_STRUCT_DIR | LFS_TYPE_DIR, .d.type = LFS_TYPE_DIR,
.d.elen = sizeof(entry->d) - 4,
.d.alen = 0,
.d.nlen = 0,
.d.u.dir[0] = lfs->root[0], .d.u.dir[0] = lfs->root[0],
.d.u.dir[1] = lfs->root[1], .d.u.dir[1] = lfs->root[1],
}; };
@@ -932,14 +845,14 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
if (((0x7f & entry->d.type) != (LFS_STRUCT_CTZ | LFS_TYPE_REG) && if (((0x7f & entry->d.type) != LFS_TYPE_REG &&
(0x7f & entry->d.type) != (LFS_STRUCT_DIR | LFS_TYPE_DIR)) || (0x7f & entry->d.type) != LFS_TYPE_DIR) ||
entry->d.nlen != pathlen) { entry->d.nlen != pathlen) {
continue; continue;
} }
int res = lfs_bd_cmp(lfs, dir->pair[0], int res = lfs_bd_cmp(lfs, dir->pair[0],
entry->off + entry->size - pathlen, entry->off + 4+entry->d.elen+entry->d.alen,
pathname, pathlen); pathname, pathlen);
if (res < 0) { if (res < 0) {
return res; return res;
@@ -952,13 +865,13 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
} }
// check that entry has not been moved // check that entry has not been moved
if (entry->d.type & LFS_STRUCT_MOVED) { if (entry->d.type & 0x80) {
int moved = lfs_moved(lfs, &entry->d.u); int moved = lfs_moved(lfs, &entry->d.u);
if (moved < 0 || moved) { if (moved < 0 || moved) {
return (moved < 0) ? moved : LFS_ERR_NOENT; return (moved < 0) ? moved : LFS_ERR_NOENT;
} }
entry->d.type &= ~LFS_STRUCT_MOVED; entry->d.type &= ~0x80;
} }
pathname += pathlen; pathname += pathlen;
@@ -968,7 +881,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
} }
// continue on if we hit a directory // continue on if we hit a directory
if ((0xf & entry->d.type) != LFS_TYPE_DIR) { if (entry->d.type != LFS_TYPE_DIR) {
return LFS_ERR_NOTDIR; return LFS_ERR_NOTDIR;
} }
@@ -1014,29 +927,22 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
dir.d.tail[0] = cwd.d.tail[0]; dir.d.tail[0] = cwd.d.tail[0];
dir.d.tail[1] = cwd.d.tail[1]; dir.d.tail[1] = cwd.d.tail[1];
err = lfs_dir_commit(lfs, &dir, NULL); err = lfs_dir_commit(lfs, &dir, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
entry.d.type = LFS_STRUCT_DIR | LFS_TYPE_DIR; entry.d.type = LFS_TYPE_DIR;
entry.d.elen = sizeof(entry.d) - 4; entry.d.elen = sizeof(entry.d) - 4;
entry.d.alen = 0; entry.d.alen = 0;
entry.d.nlen = strlen(path); entry.d.nlen = strlen(path);
entry.d.u.dir[0] = dir.pair[0]; entry.d.u.dir[0] = dir.pair[0];
entry.d.u.dir[1] = dir.pair[1]; entry.d.u.dir[1] = dir.pair[1];
entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen;
cwd.d.tail[0] = dir.pair[0]; cwd.d.tail[0] = dir.pair[0];
cwd.d.tail[1] = dir.pair[1]; cwd.d.tail[1] = dir.pair[1];
err = lfs_dir_append(lfs, &cwd, &entry, err = lfs_dir_append(lfs, &cwd, &entry, path);
&(struct lfs_region){
0, +sizeof(entry.d),
lfs_commit_mem, &entry.d, sizeof(entry.d),
&(struct lfs_region){
0, +entry.d.nlen,
lfs_commit_mem, path, entry.d.nlen}});
if (err) { if (err) {
return err; return err;
} }
@@ -1058,7 +964,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
err = lfs_dir_find(lfs, dir, &entry, &path); err = lfs_dir_find(lfs, dir, &entry, &path);
if (err) { if (err) {
return err; return err;
} else if (entry.d.type != (LFS_STRUCT_DIR | LFS_TYPE_DIR)) { } else if (entry.d.type != LFS_TYPE_DIR) {
return LFS_ERR_NOTDIR; return LFS_ERR_NOTDIR;
} }
@@ -1116,13 +1022,13 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
return (err == LFS_ERR_NOENT) ? 0 : err; return (err == LFS_ERR_NOENT) ? 0 : err;
} }
if ((0x7f & entry.d.type) != (LFS_STRUCT_CTZ | LFS_TYPE_REG) && if ((0x7f & entry.d.type) != LFS_TYPE_REG &&
(0x7f & entry.d.type) != (LFS_STRUCT_DIR | LFS_TYPE_DIR)) { (0x7f & entry.d.type) != LFS_TYPE_DIR) {
continue; continue;
} }
// check that entry has not been moved // check that entry has not been moved
if (entry.d.type & LFS_STRUCT_MOVED) { if (entry.d.type & 0x80) {
int moved = lfs_moved(lfs, &entry.d.u); int moved = lfs_moved(lfs, &entry.d.u);
if (moved < 0) { if (moved < 0) {
return moved; return moved;
@@ -1132,19 +1038,19 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
continue; continue;
} }
entry.d.type &= ~LFS_STRUCT_MOVED; entry.d.type &= ~0x80;
} }
break; break;
} }
info->type = 0xf & entry.d.type; info->type = entry.d.type;
if (info->type == LFS_TYPE_REG) { if (info->type == LFS_TYPE_REG) {
info->size = entry.d.u.file.size; info->size = entry.d.u.file.size;
} }
int err = lfs_bd_read(lfs, dir->pair[0], int err = lfs_bd_read(lfs, dir->pair[0],
entry.off + entry.size - entry.d.nlen, entry.off + 4+entry.d.elen+entry.d.alen,
info->name, entry.d.nlen); info->name, entry.d.nlen);
if (err) { if (err) {
return err; return err;
@@ -1414,25 +1320,17 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
} }
// create entry to remember name // create entry to remember name
entry.d.type = LFS_STRUCT_CTZ | LFS_TYPE_REG; entry.d.type = LFS_TYPE_REG;
entry.d.elen = sizeof(entry.d) - 4; entry.d.elen = sizeof(entry.d) - 4;
entry.d.alen = 0; entry.d.alen = 0;
entry.d.nlen = strlen(path); entry.d.nlen = strlen(path);
entry.d.u.file.head = 0xffffffff; entry.d.u.file.head = 0xffffffff;
entry.d.u.file.size = 0; entry.d.u.file.size = 0;
entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; err = lfs_dir_append(lfs, &cwd, &entry, path);
err = lfs_dir_append(lfs, &cwd, &entry,
&(struct lfs_region){
0, +sizeof(entry.d),
lfs_commit_mem, &entry.d, sizeof(entry.d),
&(struct lfs_region){
0, +entry.d.nlen,
lfs_commit_mem, path, entry.d.nlen}});
if (err) { if (err) {
return err; return err;
} }
} else if ((0xf & entry.d.type) == LFS_TYPE_DIR) { } else if (entry.d.type == LFS_TYPE_DIR) {
return LFS_ERR_ISDIR; return LFS_ERR_ISDIR;
} else if (flags & LFS_O_EXCL) { } else if (flags & LFS_O_EXCL) {
return LFS_ERR_EXIST; return LFS_ERR_EXIST;
@@ -1640,16 +1538,11 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
return err; return err;
} }
LFS_ASSERT(entry.d.type == (LFS_STRUCT_CTZ | LFS_TYPE_REG)); LFS_ASSERT(entry.d.type == LFS_TYPE_REG);
entry.d.u.file.head = file->head; entry.d.u.file.head = file->head;
entry.d.u.file.size = file->size; entry.d.u.file.size = file->size;
lfs_entry_tole32(&entry.d); err = lfs_dir_update(lfs, &cwd, &entry, NULL);
err = lfs_dir_update(lfs, &cwd, &entry,
&(struct lfs_region){
0, 0,
lfs_commit_mem, &entry.d, sizeof(entry.d)});
lfs_entry_fromle32(&entry.d);
if (err) { if (err) {
return err; return err;
} }
@@ -1934,7 +1827,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
} }
memset(info, 0, sizeof(*info)); memset(info, 0, sizeof(*info));
info->type = 0xf & entry.d.type; info->type = entry.d.type;
if (info->type == LFS_TYPE_REG) { if (info->type == LFS_TYPE_REG) {
info->size = entry.d.u.file.size; info->size = entry.d.u.file.size;
} }
@@ -1943,7 +1836,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
strcpy(info->name, "/"); strcpy(info->name, "/");
} else { } else {
err = lfs_bd_read(lfs, cwd.pair[0], err = lfs_bd_read(lfs, cwd.pair[0],
entry.off + entry.size - entry.d.nlen, entry.off + 4+entry.d.elen+entry.d.alen,
info->name, entry.d.nlen); info->name, entry.d.nlen);
if (err) { if (err) {
return err; return err;
@@ -1975,7 +1868,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
lfs_dir_t dir; lfs_dir_t dir;
if ((0xf & entry.d.type) == LFS_TYPE_DIR) { if (entry.d.type == LFS_TYPE_DIR) {
// must be empty before removal, checking size // must be empty before removal, checking size
// without masking top bit checks for any case where // without masking top bit checks for any case where
// dir is not empty // dir is not empty
@@ -1994,7 +1887,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
} }
// if we were a directory, find pred, replace tail // if we were a directory, find pred, replace tail
if ((0xf & entry.d.type) == LFS_TYPE_DIR) { if (entry.d.type == LFS_TYPE_DIR) {
int res = lfs_pred(lfs, dir.pair, &cwd); int res = lfs_pred(lfs, dir.pair, &cwd);
if (res < 0) { if (res < 0) {
return res; return res;
@@ -2004,7 +1897,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
cwd.d.tail[0] = dir.d.tail[0]; cwd.d.tail[0] = dir.d.tail[0];
cwd.d.tail[1] = dir.d.tail[1]; cwd.d.tail[1] = dir.d.tail[1];
err = lfs_dir_commit(lfs, &cwd, NULL); err = lfs_dir_commit(lfs, &cwd, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
@@ -2057,7 +1950,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
lfs_dir_t dir; lfs_dir_t dir;
if (prevexists && (0xf & preventry.d.type) == LFS_TYPE_DIR) { if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
// must be empty before removal, checking size // must be empty before removal, checking size
// without masking top bit checks for any case where // without masking top bit checks for any case where
// dir is not empty // dir is not empty
@@ -2070,11 +1963,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
// mark as moving // mark as moving
oldentry.d.type |= LFS_STRUCT_MOVED; oldentry.d.type |= 0x80;
err = lfs_dir_update(lfs, &oldcwd, &oldentry, err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
&(struct lfs_region){
0, 0,
lfs_commit_mem, &oldentry.d.type, 1});
if (err) { if (err) {
return err; return err;
} }
@@ -2087,28 +1977,16 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// move to new location // move to new location
lfs_entry_t newentry = preventry; lfs_entry_t newentry = preventry;
newentry.d = oldentry.d; newentry.d = oldentry.d;
newentry.d.type &= ~LFS_STRUCT_MOVED; newentry.d.type &= ~0x80;
newentry.d.nlen = strlen(newpath); newentry.d.nlen = strlen(newpath);
if (prevexists) { if (prevexists) {
err = lfs_dir_update(lfs, &newcwd, &newentry, err = lfs_dir_update(lfs, &newcwd, &newentry, newpath);
&(struct lfs_region){
0, 0,
lfs_commit_mem, &newentry.d, sizeof(newentry.d),
&(struct lfs_region){
sizeof(newentry.d), 0,
lfs_commit_mem, newpath, newentry.d.nlen}});
if (err) { if (err) {
return err; return err;
} }
} else { } else {
err = lfs_dir_append(lfs, &newcwd, &newentry, err = lfs_dir_append(lfs, &newcwd, &newentry, newpath);
&(struct lfs_region){
0, +sizeof(newentry.d),
lfs_commit_mem, &newentry.d, sizeof(newentry.d),
&(struct lfs_region){
0, +newentry.d.nlen,
lfs_commit_mem, newpath, newentry.d.nlen}});
if (err) { if (err) {
return err; return err;
} }
@@ -2126,7 +2004,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
} }
// if we were a directory, find pred, replace tail // if we were a directory, find pred, replace tail
if (prevexists && (0xf & preventry.d.type) == LFS_TYPE_DIR) { if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
int res = lfs_pred(lfs, dir.pair, &newcwd); int res = lfs_pred(lfs, dir.pair, &newcwd);
if (res < 0) { if (res < 0) {
return res; return res;
@@ -2136,7 +2014,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
newcwd.d.tail[0] = dir.d.tail[0]; newcwd.d.tail[0] = dir.d.tail[0];
newcwd.d.tail[1] = dir.d.tail[1]; newcwd.d.tail[1] = dir.d.tail[1];
err = lfs_dir_commit(lfs, &newcwd, NULL); err = lfs_dir_commit(lfs, &newcwd, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
@@ -2227,9 +2105,9 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
// create free lookahead // create free lookahead
memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
lfs->free.begin = 0;
lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
lfs->free.off = 0; lfs->free.off = 0;
lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
lfs->free.i = 0;
lfs_alloc_ack(lfs); lfs_alloc_ack(lfs);
// create superblock dir // create superblock dir
@@ -2246,7 +2124,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
return err; return err;
} }
err = lfs_dir_commit(lfs, &root, NULL); err = lfs_dir_commit(lfs, &root, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
@@ -2256,7 +2134,8 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
// write superblocks // write superblocks
lfs_superblock_t superblock = { lfs_superblock_t superblock = {
.d.type = LFS_STRUCT_DIR | LFS_TYPE_SUPERBLOCK, .off = sizeof(superdir.d),
.d.type = LFS_TYPE_SUPERBLOCK,
.d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4,
.d.nlen = sizeof(superblock.d.magic), .d.nlen = sizeof(superblock.d.magic),
.d.version = LFS_DISK_VERSION, .d.version = LFS_DISK_VERSION,
@@ -2273,9 +2152,10 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
lfs_superblock_tole32(&superblock.d); lfs_superblock_tole32(&superblock.d);
bool valid = false; bool valid = false;
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
err = lfs_dir_commit(lfs, &superdir, &(struct lfs_region){ err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){
sizeof(superdir.d), 0, {sizeof(superdir.d), sizeof(superblock.d),
lfs_commit_mem, &superblock.d, sizeof(superblock.d)}); &superblock.d, sizeof(superblock.d)}
}, 1);
if (err && err != LFS_ERR_CORRUPT) { if (err && err != LFS_ERR_CORRUPT) {
return err; return err;
} }
@@ -2304,9 +2184,9 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
} }
// setup free lookahead // setup free lookahead
lfs->free.begin = 0;
lfs->free.size = 0;
lfs->free.off = 0; lfs->free.off = 0;
lfs->free.size = 0;
lfs->free.i = 0;
lfs_alloc_ack(lfs); lfs_alloc_ack(lfs);
// load superblock // load superblock
@@ -2330,7 +2210,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
} }
if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); LFS_ERROR("Invalid superblock at %d %d", 0, 1);
return LFS_ERR_CORRUPT; return LFS_ERR_CORRUPT;
} }
@@ -2357,6 +2237,8 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
} }
// iterate over metadata pairs // iterate over metadata pairs
lfs_dir_t dir;
lfs_entry_t entry;
lfs_block_t cwd[2] = {0, 1}; lfs_block_t cwd[2] = {0, 1};
while (true) { while (true) {
@@ -2367,14 +2249,12 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
} }
} }
lfs_dir_t dir;
int err = lfs_dir_fetch(lfs, &dir, cwd); int err = lfs_dir_fetch(lfs, &dir, cwd);
if (err) { if (err) {
return err; return err;
} }
// iterate over contents // iterate over contents
lfs_entry_t entry;
while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) {
err = lfs_bd_read(lfs, dir.pair[0], dir.off, err = lfs_bd_read(lfs, dir.pair[0], dir.off,
&entry.d, sizeof(entry.d)); &entry.d, sizeof(entry.d));
@@ -2383,8 +2263,8 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
return err; return err;
} }
dir.off += 4 + entry.d.elen + entry.d.alen + entry.d.nlen; dir.off += lfs_entry_size(&entry);
if ((0x70 & entry.d.type) == LFS_STRUCT_CTZ) { if ((0x70 & entry.d.type) == (0x70 & LFS_TYPE_REG)) {
err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL, err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL,
entry.d.u.file.head, entry.d.u.file.size, cb, data); entry.d.u.file.head, entry.d.u.file.size, cb, data);
if (err) { if (err) {
@@ -2474,7 +2354,7 @@ static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
break; break;
} }
if (((0x70 & entry->d.type) == LFS_STRUCT_DIR) && if (((0x70 & entry->d.type) == (0x70 & LFS_TYPE_DIR)) &&
lfs_paircmp(entry->d.u.dir, dir) == 0) { lfs_paircmp(entry->d.u.dir, dir) == 0) {
return true; return true;
} }
@@ -2514,7 +2394,7 @@ static int lfs_moved(lfs_t *lfs, const void *e) {
break; break;
} }
if (!(LFS_STRUCT_MOVED & entry.d.type) && if (!(0x80 & entry.d.type) &&
memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) {
return true; return true;
} }
@@ -2539,10 +2419,7 @@ static int lfs_relocate(lfs_t *lfs,
entry.d.u.dir[0] = newpair[0]; entry.d.u.dir[0] = newpair[0];
entry.d.u.dir[1] = newpair[1]; entry.d.u.dir[1] = newpair[1];
int err = lfs_dir_update(lfs, &parent, &entry, int err = lfs_dir_update(lfs, &parent, &entry, NULL);
&(struct lfs_region){
0, 0,
lfs_commit_mem, &entry.d, sizeof(entry.d)});
if (err) { if (err) {
return err; return err;
} }
@@ -2569,7 +2446,7 @@ static int lfs_relocate(lfs_t *lfs,
parent.d.tail[0] = newpair[0]; parent.d.tail[0] = newpair[0];
parent.d.tail[1] = newpair[1]; parent.d.tail[1] = newpair[1];
return lfs_dir_commit(lfs, &parent, NULL); return lfs_dir_commit(lfs, &parent, NULL, 0);
} }
// couldn't find dir, must be new // couldn't find dir, must be new
@@ -2611,7 +2488,7 @@ int lfs_deorphan(lfs_t *lfs) {
pdir.d.tail[0] = cwd.d.tail[0]; pdir.d.tail[0] = cwd.d.tail[0];
pdir.d.tail[1] = cwd.d.tail[1]; pdir.d.tail[1] = cwd.d.tail[1];
err = lfs_dir_commit(lfs, &pdir, NULL); err = lfs_dir_commit(lfs, &pdir, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
@@ -2627,7 +2504,7 @@ int lfs_deorphan(lfs_t *lfs) {
pdir.d.tail[0] = entry.d.u.dir[0]; pdir.d.tail[0] = entry.d.u.dir[0];
pdir.d.tail[1] = entry.d.u.dir[1]; pdir.d.tail[1] = entry.d.u.dir[1];
err = lfs_dir_commit(lfs, &pdir, NULL); err = lfs_dir_commit(lfs, &pdir, NULL, 0);
if (err) { if (err) {
return err; return err;
} }
@@ -2649,7 +2526,7 @@ int lfs_deorphan(lfs_t *lfs) {
} }
// found moved entry // found moved entry
if (entry.d.type & LFS_STRUCT_MOVED) { if (entry.d.type & 0x80) {
int moved = lfs_moved(lfs, &entry.d.u); int moved = lfs_moved(lfs, &entry.d.u);
if (moved < 0) { if (moved < 0) {
return moved; return moved;
@@ -2665,11 +2542,8 @@ int lfs_deorphan(lfs_t *lfs) {
} else { } else {
LFS_DEBUG("Found partial move %d %d", LFS_DEBUG("Found partial move %d %d",
entry.d.u.dir[0], entry.d.u.dir[1]); entry.d.u.dir[0], entry.d.u.dir[1]);
entry.d.type &= ~LFS_STRUCT_MOVED; entry.d.type &= ~0x80;
err = lfs_dir_update(lfs, &cwd, &entry, err = lfs_dir_update(lfs, &cwd, &entry, NULL);
&(struct lfs_region){
0, 0,
lfs_commit_mem, &entry.d, sizeof(entry.d)});
if (err) { if (err) {
return err; return err;
} }

23
lfs.h
View File

@@ -74,15 +74,9 @@ enum lfs_error {
// File types // File types
enum lfs_type { enum lfs_type {
// file type LFS_TYPE_REG = 0x11,
LFS_TYPE_REG = 0x01, LFS_TYPE_DIR = 0x22,
LFS_TYPE_DIR = 0x02, LFS_TYPE_SUPERBLOCK = 0x2e,
LFS_TYPE_SUPERBLOCK = 0x0e,
// on disk structure
LFS_STRUCT_CTZ = 0x10,
LFS_STRUCT_DIR = 0x20,
LFS_STRUCT_MOVED = 0x80,
}; };
// File open flags // File open flags
@@ -196,7 +190,6 @@ struct lfs_info {
/// littlefs data structures /// /// littlefs data structures ///
typedef struct lfs_entry { typedef struct lfs_entry {
lfs_off_t off; lfs_off_t off;
lfs_size_t size;
struct lfs_disk_entry { struct lfs_disk_entry {
uint8_t type; uint8_t type;
@@ -250,6 +243,8 @@ typedef struct lfs_dir {
} lfs_dir_t; } lfs_dir_t;
typedef struct lfs_superblock { typedef struct lfs_superblock {
lfs_off_t off;
struct lfs_disk_superblock { struct lfs_disk_superblock {
uint8_t type; uint8_t type;
uint8_t elen; uint8_t elen;
@@ -264,9 +259,9 @@ typedef struct lfs_superblock {
} lfs_superblock_t; } lfs_superblock_t;
typedef struct lfs_free { typedef struct lfs_free {
lfs_block_t begin;
lfs_block_t size;
lfs_block_t off; lfs_block_t off;
lfs_block_t size;
lfs_block_t i;
lfs_block_t ack; lfs_block_t ack;
uint32_t *buffer; uint32_t *buffer;
} lfs_free_t; } lfs_free_t;
@@ -325,10 +320,6 @@ int lfs_remove(lfs_t *lfs, const char *path);
// If the destination exists, it must match the source in type. // If the destination exists, it must match the source in type.
// If the destination is a directory, the directory must be empty. // If the destination is a directory, the directory must be empty.
// //
// Note: If power loss occurs, it is possible that the file or directory
// will exist in both the oldpath and newpath simultaneously after the
// next mount.
//
// Returns a negative error code on failure. // Returns a negative error code on failure.
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);

View File

@@ -110,10 +110,35 @@ lfs_alloc_singleproc multiprocreuse
lfs_verify multiprocreuse lfs_verify multiprocreuse
lfs_verify singleprocreuse lfs_verify singleprocreuse
echo "--- Cleanup ---"
lfs_remove multiprocreuse lfs_remove multiprocreuse
lfs_remove singleprocreuse lfs_remove singleprocreuse
echo "--- Lookahead overflow test ---"
lfs_mkdir overflow
for name in bacon eggs pancakes
do
tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0;
// // setup lookahead to almost overflow
// lfs.free.begin = ((lfs_size_t)-1) - 2*$SIZE;
// lfs.free.size = 0;
// lfs.free.off = 0;
lfs_file_open(&lfs, &file[0], "overflow/$name",
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
size = strlen("$name");
memcpy(buffer, "$name", size);
for (int i = 0; i < $SIZE; i++) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
done
lfs_verify overflow
lfs_remove overflow
echo "--- Exhaustion test ---" echo "--- Exhaustion test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
@@ -289,7 +314,7 @@ tests/test.py << TEST
} }
lfs_file_close(&lfs, &file[0]) => 0; lfs_file_close(&lfs, &file[0]) => 0;
// open whole // open hole
lfs_remove(&lfs, "bump") => 0; lfs_remove(&lfs, "bump") => 0;
lfs_mkdir(&lfs, "splitdir") => 0; lfs_mkdir(&lfs, "splitdir") => 0;
@@ -301,5 +326,130 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Outdated lookahead test ---"
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
// fill completely with two files
lfs_file_open(&lfs, &file[0], "exhaustion1",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion2",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(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;
// rewrite one file
lfs_file_open(&lfs, &file[0], "exhaustion1",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
// rewrite second file, this requires lookahead does not
// use old population
lfs_file_open(&lfs, &file[0], "exhaustion2",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
TEST
echo "--- Outdated lookahead and split dir test ---"
rm -rf blocks
tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0;
lfs_mount(&lfs, &cfg) => 0;
// fill completely with two files
lfs_file_open(&lfs, &file[0], "exhaustion1",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
lfs_file_open(&lfs, &file[0], "exhaustion2",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4+1)/2)*(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;
// rewrite one file with a hole of one block
lfs_file_open(&lfs, &file[0], "exhaustion1",
LFS_O_WRONLY | LFS_O_TRUNC) => 0;
lfs_file_sync(&lfs, &file[0]) => 0;
size = strlen("blahblahblahblah");
memcpy(buffer, "blahblahblahblah", size);
for (lfs_size_t i = 0;
i < ((cfg.block_count-4)/2 - 1)*(cfg.block_size-8);
i += size) {
lfs_file_write(&lfs, &file[0], buffer, size) => size;
}
lfs_file_close(&lfs, &file[0]) => 0;
// try to allocate a directory, should fail!
lfs_mkdir(&lfs, "split") => LFS_ERR_NOSPC;
// file should not fail
lfs_file_open(&lfs, &file[0], "notasplit",
LFS_O_WRONLY | LFS_O_CREAT) => 0;
lfs_file_write(&lfs, &file[0], "hi", 2) => 2;
lfs_file_close(&lfs, &file[0]) => 0;
lfs_unmount(&lfs) => 0;
TEST
echo "--- Results ---" echo "--- Results ---"
tests/stats.py tests/stats.py

View File

@@ -1,13 +1,13 @@
#!/bin/bash #!/bin/bash
set -eu set -eu
echo "=== Parallel tests ===" echo "=== Interspersed tests ==="
rm -rf blocks rm -rf blocks
tests/test.py << TEST tests/test.py << TEST
lfs_format(&lfs, &cfg) => 0; lfs_format(&lfs, &cfg) => 0;
TEST TEST
echo "--- Parallel file test ---" echo "--- Interspersed file test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0;
@@ -77,7 +77,7 @@ tests/test.py << TEST
lfs_unmount(&lfs) => 0; lfs_unmount(&lfs) => 0;
TEST TEST
echo "--- Parallel remove file test ---" echo "--- Interspersed remove file test ---"
tests/test.py << TEST tests/test.py << TEST
lfs_mount(&lfs, &cfg) => 0; lfs_mount(&lfs, &cfg) => 0;
lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0; lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0;