WIP added wip journalling things for dirs

This commit is contained in:
Christopher Haster
2018-05-19 18:25:47 -05:00
parent c8323112ee
commit 57fbc52cfc
3 changed files with 932 additions and 3 deletions

901
lfs.c
View File

@@ -221,9 +221,7 @@ static int lfs_cache_prog(lfs_t *lfs, lfs_cache_t *pcache,
/// General lfs block device operations ///
static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
// if we ever do more than writes to alternating pairs,
// this may need to consider pcache
return lfs_cache_read(lfs, &lfs->rcache, NULL,
return lfs_cache_read(lfs, &lfs->rcache, &lfs->pcache,
block, off, buffer, size);
}
@@ -427,6 +425,903 @@ static inline bool lfs_pairsync(
(paira[0] == pairb[1] && paira[1] == pairb[0]);
}
enum {
LFS_TAG_VALID = (int)0x80000000,
LFS_TAG_TYPE = (int)0x7fc00000,
LFS_TAG_ID = (int)0x001ff000,
LFS_TAG_SIZE = (int)0x00000fff,
};
static inline uint32_t lfs_mktag(uint16_t type, uint16_t id, uint16_t size) {
return (type << 22) | (id << 12) | size;
}
static inline bool lfs_tag_valid(uint32_t tag) {
return !(tag & 0x80000000);
}
static inline uint32_t lfs_tag_uid(uint32_t tag) {
return (tag & 0x7fdff000) >> 12;
}
static inline uint16_t lfs_tag_type(uint32_t tag) {
return (tag & 0x7fc00000) >> 22;
}
static inline uint16_t lfs_tag_id(uint32_t tag) {
return (tag & 0x001ff000) >> 12;
}
static uint16_t lfs_tag_size(uint32_t tag) {
return tag & 0x00000fff;
}
struct lfs_region__ {
uint32_t tag;
union {
void *buffer;
struct {
lfs_block_t block;
lfs_off_t off;
} d;
} u;
};
struct lfs_commit {
lfs_block_t block;
lfs_off_t off;
lfs_off_t begin;
lfs_off_t end;
uint32_t ptag;
uint32_t crc;
struct {
int16_t id;
uint16_t type;
} compact;
};
static int lfs_commit_traverse(lfs_t *lfs, struct lfs_commit *commit,
int (*cb)(lfs_t *lfs, void *data, struct lfs_region__ region),
void *data) {
// iterate over dir block backwards (for faster lookups)
lfs_block_t block = commit->block;
lfs_off_t off = commit->off;
uint32_t tag = commit->ptag;
while (off != sizeof(uint32_t)) {
//printf("read %#010x at %x:%x\n", tag, block, off);
int err = cb(lfs, data, (struct lfs_region__){
.tag=(0x80000000 | tag),
.u.d.block=block,
.u.d.off=off-lfs_tag_size(tag)});
if (err) {
return err;
}
LFS_ASSERT(off > sizeof(uint32_t)+lfs_tag_size(tag));
off -= sizeof(uint32_t)+lfs_tag_size(tag);
uint32_t ntag;
err = lfs_bd_read(lfs, block, off, &ntag, sizeof(ntag));
if (err) {
return err;
}
tag ^= lfs_fromle32(ntag);
}
return 0;
}
static int lfs_commit_compactcheck(lfs_t *lfs, void *p,
struct lfs_region__ region) {
struct lfs_commit *commit = p;
if (lfs_tag_id(region.tag) != commit->compact.id) {
return 1;
} else if (lfs_tag_type(region.tag) == commit->compact.type) {
return 2;
}
return 0;
}
static int lfs_commit_commit(lfs_t *lfs,
struct lfs_commit *commit, struct lfs_region__ region) {
// request for compaction?
if (commit->compact.id >= 0) {
if (lfs_tag_id(region.tag) != commit->compact.id) {
// ignore non-matching ids
return 0;
}
commit->compact.type = lfs_tag_type(region.tag);
int res = lfs_commit_traverse(lfs, commit,
lfs_commit_compactcheck, commit);
//printf("traverse(%d, %#010x) -> %d\n", commit->compact.id, region.tag, res);
if (res < 0) {
return res;
}
if (res == 2) {
//printf("ignoring %#010x at %x:%x\n", region.tag, commit->block, commit->off);
// already committed
return 0;
}
}
// check if we fit
lfs_size_t size = lfs_tag_size(region.tag);
//printf("writing %#010x at %x:%x\n", region.tag, commit->block, commit->off);
if (commit->off + sizeof(uint32_t)+size > commit->end) {
return LFS_ERR_NOSPC;
}
// write out tag
uint32_t tag = lfs_tole32((region.tag & 0x7fffffff) ^ commit->ptag);
lfs_crc(&commit->crc, &tag, sizeof(tag));
int err = lfs_bd_prog(lfs, commit->block, commit->off, &tag, sizeof(tag));
if (err) {
return err;
}
commit->off += sizeof(tag);
if (!(region.tag & 0x80000000)) {
// from memory
lfs_crc(&commit->crc, region.u.buffer, size);
err = lfs_bd_prog(lfs, commit->block, commit->off,
region.u.buffer, size);
if (err) {
return err;
}
} else {
// from disk
for (lfs_off_t i = 0; i < size; i++) {
uint8_t dat;
int err = lfs_bd_read(lfs,
region.u.d.block, region.u.d.off+i, &dat, 1);
if (err) {
return err;
}
lfs_crc(&commit->crc, &dat, 1);
err = lfs_bd_prog(lfs, commit->block, commit->off+i, &dat, 1);
if (err) {
return err;
}
}
}
commit->off += size;
commit->ptag = region.tag;
return 0;
}
static int lfs_commit_crc(lfs_t *lfs, struct lfs_commit *commit) {
// align to program units
lfs_off_t noff = lfs_alignup(
commit->off + 2*sizeof(uint32_t), lfs->cfg->prog_size);
// read erased state from next program unit
uint32_t tag;
int err = lfs_bd_read(lfs, commit->block, noff, &tag, sizeof(tag));
if (err) {
return err;
}
// build crc tag
tag = (0x80000000 & ~lfs_fromle32(tag)) |
lfs_mktag(LFS_TYPE_CRC_, 0x1ff,
noff - (commit->off+sizeof(uint32_t)));
// write out crc
uint32_t footer[2];
footer[0] = lfs_tole32(tag ^ commit->ptag);
lfs_crc(&commit->crc, &footer[0], sizeof(footer[0]));
footer[1] = lfs_tole32(commit->crc);
err = lfs_bd_prog(lfs, commit->block, commit->off,
footer, sizeof(footer));
if (err) {
return err;
}
commit->off += sizeof(uint32_t)+lfs_tag_size(tag);
commit->ptag = tag;
// flush buffers
err = lfs_bd_sync(lfs);
if (err) {
return err;
}
// successful commit, check checksum to make sure
uint32_t crc = 0xffffffff;
err = lfs_bd_crc(lfs, commit->block, commit->begin,
commit->off-lfs_tag_size(tag) - commit->begin, &crc);
if (err) {
return err;
}
if (crc != commit->crc) {
return LFS_ERR_CORRUPT;
}
return 0;
}
/*static*/ int lfs_dir_alloc_(lfs_t *lfs, lfs_dir_t_ *dir) {
// allocate pair of dir blocks (backwards, so we write to block 1 first)
for (int i = 0; i < 2; i++) {
int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]);
if (err) {
return err;
}
}
// rather than clobbering one of the blocks we just pretend
// the revision may be valid
int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->rev, 4);
dir->rev = lfs_fromle32(dir->rev);
if (err) {
return err;
}
// set defaults
dir->off = sizeof(dir->rev);
dir->etag = 0;
dir->count = 0;
dir->erased = false;
dir->tail[0] = 0xffffffff;
dir->tail[1] = 0xffffffff;
// don't write out yet, let caller take care of that
return 0;
}
/*static*/ int lfs_dir_fetch_(lfs_t *lfs,
lfs_dir_t_ *dir, const lfs_block_t pair[2],
int (*cb)(lfs_t *lfs, void *data, struct lfs_region__ region),
void *data) {
dir->pair[0] = pair[0];
dir->pair[1] = pair[1];
// find the block with the most recent revision
uint32_t rev[2];
for (int i = 0; i < 2; i++) {
int err = lfs_bd_read(lfs, dir->pair[i], 0, &rev[i], sizeof(rev[i]));
rev[i] = lfs_fromle32(rev[i]);
if (err) {
return err;
}
}
if (lfs_scmp(rev[1], rev[0]) > 0) {
lfs_pairswap(dir->pair);
lfs_pairswap(rev);
}
// load blocks and check crc
for (int i = 0; i < 2; i++) {
lfs_off_t off = sizeof(dir->rev);
uint32_t ptag = 0;
uint32_t crc = 0xffffffff;
dir->rev = lfs_tole32(rev[0]);
lfs_crc(&crc, &dir->rev, sizeof(dir->rev));
dir->rev = lfs_fromle32(dir->rev);
while (true) {
// extract next tag
uint32_t tag;
int err = lfs_bd_read(lfs, dir->pair[0], off, &tag, sizeof(tag));
if (err) {
return err;
}
lfs_crc(&crc, &tag, sizeof(tag));
tag = lfs_fromle32(tag) ^ ptag;
printf("tag %#010x (%x:%x %#010x)\n", tag, dir->pair[0], off, lfs_tag_type(ptag));
// next commit not yet programmed
if (lfs_tag_type(ptag) == LFS_TYPE_CRC_ && lfs_tag_valid(tag)) {
dir->erased = true;
return 0;
}
// check we're in valid range
if (off + sizeof(uint32_t)+lfs_tag_size(tag) >
lfs->cfg->block_size - 2*sizeof(uint32_t)) {
break;
}
if (lfs_tag_type(tag) == LFS_TYPE_CRC_) {
// check the crc entry
uint32_t dcrc;
int err = lfs_bd_read(lfs, dir->pair[0],
off+sizeof(uint32_t), &dcrc, sizeof(dcrc));
if (err) {
return err;
}
if (crc != lfs_fromle32(dcrc)) {
if (off == sizeof(dir->rev)) {
// try other block
break;
} else {
// consider what we have good enough
dir->erased = false;
return 0;
}
}
dir->off = off + sizeof(uint32_t)+lfs_tag_size(tag);
dir->etag = tag;
crc = 0xffffffff;
} else {
err = lfs_bd_crc(lfs, dir->pair[0],
off+sizeof(uint32_t), lfs_tag_size(tag), &crc);
if (err) {
return err;
}
if (cb) {
err = cb(lfs, data, (struct lfs_region__){
.tag=(tag | 0x80000000),
.u.d.block=dir->pair[0],
.u.d.off=off+sizeof(uint32_t)});
if (err) {
return err;
}
}
}
ptag = tag;
off += sizeof(uint32_t)+lfs_tag_size(tag);
}
// failed, try the other crc?
lfs_pairswap(dir->pair);
lfs_pairswap(rev);
}
LFS_ERROR("Corrupted dir pair at %d %d", dir->pair[0], dir->pair[1]);
return LFS_ERR_CORRUPT;
}
static int lfs_dir_traverse2_(lfs_t *lfs, lfs_dir_t_ *dir,
int (*cb)(lfs_t *lfs, void *data, struct lfs_region__ region),
void *data) {
return lfs_commit_traverse(lfs, &(struct lfs_commit){
.block=dir->pair[0], .off=dir->off, .ptag=dir->etag},
cb, data);
}
struct lfs_dir_getter {
uint32_t tag;
uint32_t mask;
void *buffer;
};
static int lfs_dir_getter(lfs_t *lfs, void *p, struct lfs_region__ region) {
struct lfs_dir_getter *getter = p;
if ((region.tag & getter->mask) == (getter->tag & getter->mask)) {
lfs_size_t size = lfs_tag_size(getter->tag);
if (lfs_tag_size(region.tag) > size) {
return LFS_ERR_RANGE;
}
int err = lfs_bd_read(lfs, region.u.d.block, region.u.d.off,
getter->buffer, size);
if (err) {
return err;
}
memset((uint8_t*)getter->buffer + size, 0,
lfs_tag_size(region.tag) - size);
getter->tag |= region.tag & ~0x80000fff;
return true;
}
return false;
}
/*static*/ int32_t lfs_dir_get_(lfs_t *lfs, lfs_dir_t_ *dir,
uint32_t tag, uint32_t mask, void *buffer) {
struct lfs_dir_getter getter = {tag, mask, buffer};
int res = lfs_dir_traverse2_(lfs, dir, lfs_dir_getter, &getter);
if (res < 0) {
return res;
}
if (!res) {
return LFS_ERR_NOENT;
}
return getter.tag;
}
struct lfs_dir_mover {
// traversal things
lfs_dir_t_ *dir;
int (*cb)(lfs_t *lfs, void *data, struct lfs_commit *commit);
void *data;
// ids to iterate through
uint16_t begin;
uint16_t end;
uint16_t ack;
};
static int lfs_dir_mover_commit(lfs_t *lfs, void *p,
struct lfs_region__ region) {
return lfs_commit_commit(lfs, p, region);
}
int lfs_dir_mover(lfs_t *lfs, void *p, struct lfs_commit *commit) {
struct lfs_dir_mover *mover = p;
for (int i = mover->begin; i < mover->end; i++) {
// tell the committer to check for duplicates
uint16_t old = commit->compact.id;
if (commit->compact.id < 0) {
commit->compact.id = i;
}
// commit pending commits
int err = mover->cb(lfs, mover->data, commit);
if (err) {
commit->compact.id = old;
return err;
}
// iterate over on-disk regions
err = lfs_dir_traverse2_(lfs, mover->dir,
lfs_dir_mover_commit, commit);
if (err) {
commit->compact.id = old;
return err;
}
mover->ack = i;
commit->compact.id = old;
}
return 0;
}
/*static*/ int lfs_dir_compact2_(lfs_t *lfs, lfs_dir_t_ *dir,
int (*cb)(lfs_t *lfs, void *data, struct lfs_commit *commit),
void *data) {
// save some state in case block is bad
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
bool relocated = false;
// increment revision count
dir->rev += 1;
while (true) {
// setup mover
struct lfs_dir_mover mover = {
.dir = dir,
.cb = cb,
.data = data,
.begin = 0,
.end = dir->count,
.ack = 0,
};
if (true) {
// erase block to write to
int err = lfs_bd_erase(lfs, dir->pair[1]);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
// write out header
uint32_t crc = 0xffffffff;
uint32_t rev = lfs_tole32(dir->rev);
lfs_crc(&crc, &rev, sizeof(rev));
err = lfs_bd_prog(lfs, dir->pair[1], 0, &rev, sizeof(rev));
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
// setup compaction
struct lfs_commit commit = {
.block = dir->pair[1],
.off = sizeof(dir->rev),
// leave space for tail pointer
.begin = 0,
.end = lfs_min(lfs->cfg->block_size - 5*sizeof(uint32_t),
lfs_alignup(lfs->cfg->block_size / 2,
lfs->cfg->prog_size)),
.crc = crc,
.ptag = 0,
.compact.id = -1,
};
// run compaction over mover
err = lfs_dir_mover(lfs, &mover, &commit);
if (err) {
if (err == LFS_ERR_NOSPC) {
goto split;
} else if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
if (!lfs_pairisnull(dir->tail)) {
// TODO le32
commit.end = lfs->cfg->block_size - 2*sizeof(uint32_t),
err = lfs_commit_commit(lfs, &commit, (struct lfs_region__){
.tag=lfs_mktag(LFS_TYPE_TAIL_, 0x1ff,
sizeof(dir->tail)),
.u.buffer=dir->tail});
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
}
err = lfs_commit_crc(lfs, &commit);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
// successful compaction, swap dir pair to indicate most recent
lfs_pairswap(dir->pair);
dir->off = commit.off;
dir->etag = commit.ptag;
dir->erased = true;
}
break;
split:
// commit no longer fits, need to split dir
dir->count = mover.ack;
mover.begin = mover.ack+1;
// drop caches and create tail
lfs->pcache.block = 0xffffffff;
lfs_dir_t_ tail;
int err = lfs_dir_alloc_(lfs, &tail);
if (err) {
return err;
}
tail.tail[0] = dir->tail[0];
tail.tail[1] = dir->tail[1];
err = lfs_dir_compact2_(lfs, &tail, lfs_dir_mover, &mover);
if (err) {
return err;
}
dir->tail[0] = tail.pair[0];
dir->tail[1] = tail.pair[1];
continue;
relocate:
//commit was corrupted
LFS_DEBUG("Bad block at %d", dir->pair[1]);
// drop caches and prepare to relocate block
relocated = true;
lfs->pcache.block = 0xffffffff;
// can't relocate superblock, filesystem is now frozen
if (lfs_paircmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) {
LFS_WARN("Superblock %d has become unwritable", oldpair[1]);
return LFS_ERR_CORRUPT;
}
// relocate half of pair
err = lfs_alloc(lfs, &dir->pair[1]);
if (err) {
return err;
}
continue;
}
if (relocated) {
// update references if we relocated
LFS_DEBUG("Relocating %d %d to %d %d",
oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
int err = lfs_relocate(lfs, oldpair, dir->pair);
if (err) {
return err;
}
}
// shift over any directories that are affected
for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
if (lfs_paircmp(d->pair, dir->pair) == 0) {
d->pair[0] = dir->pair[0];
d->pair[1] = dir->pair[1];
}
}
return 0;
}
/*static*/ int lfs_dir_commit2_(lfs_t *lfs, lfs_dir_t_ *dir,
int (*cb)(lfs_t *lfs, void *data, struct lfs_commit *commit),
void *data) {
if (!dir->erased) {
// not erased, must compact
return lfs_dir_compact2_(lfs, dir, cb, data);
}
struct lfs_commit commit = {
.block = dir->pair[0],
.begin = dir->off,
.off = dir->off,
.end = lfs->cfg->block_size - 2*sizeof(uint32_t),
.crc = 0xffffffff,
.ptag = dir->etag,
.compact.id = -1,
};
int err = cb(lfs, data, &commit);
if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
return lfs_dir_compact2_(lfs, dir, cb, data);
}
return err;
}
err = lfs_commit_crc(lfs, &commit);
if (err) {
if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
return lfs_dir_compact2_(lfs, dir, cb, data);
}
return err;
}
// successful commit, lets update dir
dir->off = commit.off;
dir->etag = commit.ptag;
return 0;
}
struct lfs_dir_commit_regions {
const struct lfs_region__ *regions;
int count;
};
int lfs_dir_commit_regions(lfs_t *lfs, void *p, struct lfs_commit *commit) {
struct lfs_dir_commit_regions *region = p;
for (int i = 0; i < region->count; i++) {
int err = lfs_commit_commit(lfs, commit, region->regions[i]);
if (err) {
return err;
}
}
return 0;
}
// TODO rm me, just for testing
/*static*/ int lfs_dir_compact_(lfs_t *lfs, lfs_dir_t_ *dir,
const struct lfs_region__ *regions, int count) {
return lfs_dir_compact2_(lfs, dir, lfs_dir_commit_regions,
&(struct lfs_dir_commit_regions){regions, count});
}
/*static*/ int lfs_dir_commit_(lfs_t *lfs, lfs_dir_t_ *dir,
const struct lfs_region__ *regions, int count) {
return lfs_dir_commit2_(lfs, dir, lfs_dir_commit_regions,
&(struct lfs_dir_commit_regions){regions, count});
}
/*static*/ int lfs_dir_add(lfs_t *lfs, lfs_dir_t_ *dir) {
uint16_t id = dir->count;
dir->count += 1;
return id;
}
/*static*/ int lfs_dir_drop(lfs_t *lfs, lfs_dir_t_ *dir, uint16_t id) {
dir->count -= 1;
// TODO compact during traverse when compacting?
return lfs_dir_commit_(lfs, dir, (struct lfs_region__[]){{
lfs_mktag(LFS_TYPE_DROP_, id, 0)}}, 1);
}
struct lfs_dir_setter {
const struct lfs_region__ *regions;
int count;
};
int lfs_dir_setter(lfs_t *lfs, void *p, struct lfs_commit *commit) {
struct lfs_dir_setter *setter = p;
for (int i = 0; i < setter->count; i++) {
int err = lfs_commit_commit(lfs, commit, setter->regions[i]);
if (err) {
return err;
}
}
return 0;
}
/*static*/ int lfs_dir_set_(lfs_t *lfs, lfs_dir_t_ *dir,
const struct lfs_region__ *regions, int count) {
return lfs_dir_commit2_(lfs, dir, lfs_dir_setter,
&(struct lfs_dir_setter){regions, count});
}
struct lfs_dir_lookuper {
const char *name;
lfs_size_t size;
lfs_block_t pair[2];
uint32_t tag;
lfs_block_t tail[2];
};
static int lfs_dir_lookuper(lfs_t *lfs, void *p, struct lfs_region__ region) {
struct lfs_dir_lookuper *lookup = p;
if (lfs_tag_type(region.tag) == LFS_TYPE_NAME_ &&
lfs_tag_size(region.tag) == lookup->size) {
int res = lfs_bd_cmp(lfs, region.u.d.block, region.u.d.off,
lookup->name, lookup->size);
if (res < 0) {
return res;
}
if (res) {
// found a match
lookup->tag = region.tag & ~0x80000000;
lookup->pair[0] = 0xffffffff;
lookup->pair[1] = 0xffffffff;
}
}
if (lfs_tag_valid(lookup->tag) &&
lfs_tag_id(region.tag) == lfs_tag_id(lookup->tag) &&
lfs_tag_type(region.tag) == LFS_TYPE_DIR_) {
int err = lfs_bd_read(lfs, region.u.d.block, region.u.d.off,
lookup->pair, sizeof(lookup->pair));
if (err) {
return err;
}
}
if (lfs_tag_type(lookup->tag) == LFS_TYPE_TAIL_) {
int err = lfs_bd_read(lfs, region.u.d.block, region.u.d.off,
lookup->tail, sizeof(lookup->tail));
if (err) {
return err;
}
}
return 0;
}
/*static*/ int32_t lfs_dir_lookup_(lfs_t *lfs, lfs_dir_t_ *dir,
const char **path) {
struct lfs_dir_lookuper lookup = {
.name = *path,
.pair[0] = 4, // TODO make superblock
.pair[1] = 5,
};
while (true) {
nextname:
// skip slashes
lookup.name += strspn(lookup.name, "/");
lookup.size = strcspn(lookup.name, "/");
// special case for root dir
if (lookup.name[0] == '\0') {
// TODO set up root?
// *entry = (lfs_entry_t){
// .d.type = LFS_STRUCT_DIR | LFS_TYPE_DIR,
// .d.u.dir[0] = lfs->root[0],
// .d.u.dir[1] = lfs->root[1],
// };
return lfs_mktag(LFS_TYPE_DIR_, 0x1ff, 0);
}
// skip '.' and root '..'
if ((lookup.size == 1 && memcmp(lookup.name, ".", 1) == 0) ||
(lookup.size == 2 && memcmp(lookup.name, "..", 2) == 0)) {
lookup.name += lookup.size;
goto nextname;
}
// skip if matched by '..' in name
const char *suffix = lookup.name + lookup.size;
lfs_size_t suffsize;
int depth = 1;
while (true) {
suffix += strspn(suffix, "/");
suffsize = strcspn(suffix, "/");
if (suffsize == 0) {
break;
}
if (suffsize == 2 && memcmp(suffix, "..", 2) == 0) {
depth -= 1;
if (depth == 0) {
lookup.name = suffix + suffsize;
goto nextname;
}
} else {
depth += 1;
}
suffix += suffsize;
}
// update what we've found
*path = lookup.name;
// find path // TODO handle tails
while (true) {
lookup.tag = 0xffffffff;
lookup.tail[0] = 0xffffffff;
lookup.tail[1] = 0xffffffff;
int err = lfs_dir_fetch_(lfs, dir, lookup.pair,
lfs_dir_lookuper, &lookup);
if (err) {
return err;
}
if (lfs_tag_valid(lookup.tag)) {
// found it
break;
}
if (lfs_pairisnull(lookup.tail)) {
return LFS_ERR_NOENT;
}
lookup.pair[0] = lookup.tail[0];
lookup.pair[1] = lookup.tail[1];
}
// TODO handle moves
// // check that entry has not been moved
// if (entry->d.type & LFS_STRUCT_MOVED) {
// int moved = lfs_moved(lfs, &entry->d.u);
// if (moved < 0 || moved) {
// return (moved < 0) ? moved : LFS_ERR_NOENT;
// }
//
// entry->d.type &= ~LFS_STRUCT_MOVED;
// }
lookup.name += lookup.size;
lookup.name += strspn(lookup.name, "/");
if (lookup.name[0] == '\0') {
return lookup.tag;
}
// continue on if we hit a directory
// TODO update with what's on master?
if (lfs_tag_type(lookup.tag) != LFS_TYPE_DIR_) {
return LFS_ERR_NOTDIR;
}
}
}
//////////////////////////////////////////////////////////
static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
// allocate pair of dir blocks
for (int i = 0; i < 2; i++) {

29
lfs.h
View File

@@ -105,6 +105,24 @@ enum lfs_type {
LFS_STRUCT_DIR = 0x20,
LFS_STRUCT_INLINE = 0x30,
LFS_STRUCT_MOVED = 0x80,
LFS_TYPE_CTZ_ = 0x1,
LFS_TYPE_DIR_ = 0x2,
LFS_TYPE_INLINE_ = 0x3,
LFS_TYPE_ATTR_ = 0x4,
LFS_TYPE_NAME_ = 0x5,
LFS_TYPE_DROP_ = 0x7,
LFS_TYPE_TAIL_ = 0xd,
LFS_TYPE_CRC_ = 0xe,
// LFS_TYPE_DIR_ = 0x002,
// LFS_TYPE_SUPERBLOCK_ = 0xff2,
// LFS_MASK_ID_ = 0xff000000,
// LFS_MASK_TYPE_ = 0x00fff000,
// LFS_MASK_ATTR_ = 0x00ff0000,
// LFS_MASK_STRUCT_ = 0x0000f000,
// LFS_MASK_SIZE_ = 0x00000fff,
};
// File open flags
@@ -314,6 +332,17 @@ typedef struct lfs_dir {
} d;
} lfs_dir_t;
typedef struct lfs_dir_ {
lfs_block_t pair[2];
lfs_block_t tail[2];
uint32_t rev;
lfs_off_t off;
uint32_t etag;
uint16_t count;
bool erased;
} lfs_dir_t_;
typedef struct lfs_superblock {
struct lfs_disk_superblock {
lfs_block_t root[2];

View File

@@ -161,6 +161,11 @@ static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
}
// Align to nearest multiple of a size
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return (a + alignment-1) - ((a + alignment-1) % alignment);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);