Added lfsr_remove

In theory this is pretty much the same as lfsr_mkdir, but backwards.

The main work was making the interactions between removing mids/rids and
the grm correct. This ends up meaning we just need to update the grm on
any mid/rid update the same way we update the list of opened mdirs.

On the plus side, it turned out to be possible to deduplicate the mdir
uninlining route a bit, by adding range argument to lfsr_mdir_commit_
and changing the write of the newly uninlined mtree/mdir to marking
mtree as dirty and then joining the common path.

This lets us move the pre-commit round of grm updates into a single
location in lfsr_mdir_commit, removing and extra function definition and
the related state marshalling while also simplifying the control-flow.

This also raises the question, can more lfsr_mdir_commit be deduplicated
more? Uninlining is a infrequent operation we don't really need to
optimize for.

---

Testing lfsr_remove also found a bug related to incorrect propagation of
when the mroot becomes "unerased" (when rbyd overflows). This raises the
concern that we're not propagating unerased-states very rigorously, and
unexpected errors may not allow the filesystem to resume.

This has never been in a very good place for littlefs, but would be
worth improving in the future.
This commit is contained in:
Christopher Haster
2023-07-19 22:15:45 -05:00
parent b935579f52
commit 53a4da13f5
3 changed files with 1287 additions and 181 deletions
+285 -177
View File
@@ -1653,7 +1653,7 @@ static lfs_ssize_t lfsr_grm_fromdisk(lfs_t *lfs, lfsr_grm_t *grm,
// adjust mid if mtree is inlined
if (lfsr_mtree_isinlined(lfs)) {
LFS_ASSERT(mid == 0);
mid = -1;
mid = LFSR_MID_MROOT;
}
grm->mid = mid;
@@ -1676,64 +1676,6 @@ static inline int lfsr_grm_xor(lfs_t *lfs,
return lfsr_gdelta_xor(lfs, gdelta, LFSR_GRM_DSIZE, xor);
}
// fix grm if a split occurs
static int lfsr_grm_split(lfs_t *lfs,
uint8_t gdelta[LFSR_GRM_DSIZE],
lfsr_data_t xor,
lfs_ssize_t split_mid, lfs_size_t split_rid) {
// the interaction between the mtree/grm is really annoying, we need
// to fix outdated mids/rids caused by the split before propagating
// any commits
//
// this means decoding any grms, reencoding, and xoring against
// the pending grm delta
lfsr_grm_t grm;
lfs_ssize_t d = lfsr_grm_fromdisk(lfs, &grm, xor);
if (d < 0) {
return d;
}
if (grm.mid == split_mid) {
// TODO do we need this if we allow mid=0 => mroot when inlined?
// update mid if we are uninlining
grm.mid = lfs_smax32(split_mid, 0);
if (grm.rid >= split_rid) {
grm.mid += 1;
grm.rid -= split_rid;
}
} else if (grm.mid > split_mid) {
grm.mid += 1;
}
// zero so we don't end up with trailing garbage
uint8_t buf[LFSR_GRM_DSIZE];
memset(buf, 0, LFSR_GRM_DSIZE);
d = lfsr_grm_todisk(lfs, &grm, buf);
if (d < 0) {
return d;
}
// assume we already xored our gdelta with the grm, so we need to
// xor the grm out of the gdelta
//
// gd' = gd xor (grm' xor grm)
//
int err = lfsr_grm_xor(lfs, buf, xor);
if (err) {
return err;
}
err = lfsr_grm_xor(lfs, gdelta, LFSR_DATA_BUF(buf, LFSR_GRM_DSIZE));
if (err) {
return err;
}
return 0;
}
/// Internal operations predeclared here ///
//#ifndef LFS_READONLY
@@ -1790,6 +1732,7 @@ static void lfs_alloc_ack(lfs_t *lfs);
// and our main "fix everything before writing" function
static int lfsr_fs_preparemutation(lfs_t *lfs);
static int lfsr_fs_fixgrm(lfs_t *lfs);
/// Red-black-yellow Dhara tree operations ///
@@ -3224,7 +3167,7 @@ failed:;
//
// if rbyd does not fit, a good split_id is returned
static int lfsr_rbyd_estimate(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
lfs_ssize_t start_id, lfs_size_t threshold,
lfs_ssize_t start_id, lfs_ssize_t end_id, lfs_size_t threshold,
lfs_size_t *split_id_) {
#ifndef LFSR_NO_REBALANCE
lfs_size_t dsize = 0;
@@ -3239,7 +3182,7 @@ static int lfsr_rbyd_estimate(lfs_t *lfs, const lfsr_rbyd_t *rbyd,
if (err && err != LFS_ERR_NOENT) {
return err;
}
if (err == LFS_ERR_NOENT) {
if (err == LFS_ERR_NOENT || (end_id >= 0 && id >= end_id)) {
return true;
}
@@ -4042,7 +3985,7 @@ static int lfsr_btree_commit(lfs_t *lfs,
// check if we're within our compaction threshold, otherwise we
// need to split
int fits = lfsr_rbyd_estimate(lfs, rbyd, -1,
int fits = lfsr_rbyd_estimate(lfs, rbyd, -1, -1,
lfs->cfg->block_size/2,
&split_id);
if (fits < 0) {
@@ -4349,7 +4292,7 @@ static int lfsr_btree_commit(lfs_t *lfs,
// this is imprecise when not compacting, so we may still fail to
// merge, but this at least lets us avoid wasting programming cycles
// when merge failure is obvious
int fits = lfsr_rbyd_estimate(lfs, &sibling, -1,
int fits = lfsr_rbyd_estimate(lfs, &sibling, -1, -1,
lfs->cfg->block_size/4,
NULL);
if (fits < 0) {
@@ -5376,10 +5319,10 @@ static int lfsr_mtree_parent(lfs_t *lfs, lfsr_mptr_t mchild,
}
// low-level mdir compaction
static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir_,
lfs_ssize_t mid,
lfs_ssize_t start_id, lfs_ssize_t end_id,
const lfsr_mdir_t *msource,
const lfsr_mdir_t *mdir,
const lfsr_attr_t *attr1s, lfs_size_t attr1_count,
const lfsr_attr_t *attr2s, lfs_size_t attr2_count) {
// note mid indicates some special cases:
@@ -5389,7 +5332,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// first thing we need to do is read our current revision count
uint32_t rev;
int err = lfsr_bd_read(lfs, msource->rbyd.block, 0, sizeof(uint32_t),
int err = lfsr_bd_read(lfs, mdir->rbyd.block, 0, sizeof(uint32_t),
&rev, sizeof(uint32_t));
if (err && err != LFS_ERR_CORRUPT) {
return err;
@@ -5403,8 +5346,8 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// TODO rev things
&& (rev + 1) % lfs->cfg->block_cycles == 0))) {
// allocate a new mdir for relocation
err = lfsr_mdir_alloc(lfs, mdir,
(mid != LFSR_MID_WL ? mid : mdir->mid));
err = lfsr_mdir_alloc(lfs, mdir_,
(mid != LFSR_MID_WL ? mid : mdir_->mid));
if (err) {
return err;
}
@@ -5413,7 +5356,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
//
// we use whatever is on-disk to avoid needing to rewrite the
// redund block
err = lfsr_bd_read(lfs, mdir->rbyd.block, 0, sizeof(uint32_t),
err = lfsr_bd_read(lfs, mdir_->rbyd.block, 0, sizeof(uint32_t),
&rev, sizeof(uint32_t));
if (err && err != LFS_ERR_CORRUPT) {
return err;
@@ -5429,35 +5372,35 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// our mdir to avoid losing info
//
// if succesful, this should get immediately appended to our new commit
int err = lfsr_fs_consumegdelta(lfs, msource);
int err = lfsr_fs_consumegdelta(lfs, mdir);
if (err) {
return err;
}
}
// swap our rbyds
lfs_swap32(&mdir->rbyd.block, &mdir->redund_block);
lfs_swap32(&mdir_->rbyd.block, &mdir_->redund_block);
// update our revision count
mdir->rbyd.off = 0;
mdir->rbyd.trunk = 0;
mdir->rbyd.weight = 0;
mdir->rbyd.crc = 0;
mdir_->rbyd.off = 0;
mdir_->rbyd.trunk = 0;
mdir_->rbyd.weight = 0;
mdir_->rbyd.crc = 0;
// erase, preparing for compact
err = lfsr_bd_erase(lfs, mdir->rbyd.block);
err = lfsr_bd_erase(lfs, mdir_->rbyd.block);
if (err) {
return err;
}
// increment our revision count and write it to our rbyd
// TODO rev things
err = lfsr_rbyd_appendrev(lfs, &mdir->rbyd, rev + 1);
err = lfsr_rbyd_appendrev(lfs, &mdir_->rbyd, rev + 1);
if (err) {
return err;
}
// copy over attrs
err = lfsr_rbyd_compact(lfs, &mdir->rbyd, start_id, end_id, &msource->rbyd);
err = lfsr_rbyd_compact(lfs, &mdir_->rbyd, start_id, end_id, &mdir->rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -5467,7 +5410,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
//
// upper layers should make sure this can't fail by limiting the
// maximum commit size
err = lfsr_rbyd_appendall(lfs, &mdir->rbyd, start_id, end_id,
err = lfsr_rbyd_appendall(lfs, &mdir_->rbyd, start_id, end_id,
attr1s, attr1_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -5476,7 +5419,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// note we don't filter attrs from our second pending list, this
// is used for some auxiliary attrs in lfsr_mdir_commit
err = lfsr_rbyd_appendall(lfs, &mdir->rbyd, -1, -1,
err = lfsr_rbyd_appendall(lfs, &mdir_->rbyd, -1, -1,
attr2s, attr2_count);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -5485,7 +5428,7 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// drop commit if weight goes to zero
if (mdir->mid >= 0 && mdir->rbyd.weight == 0) {
if (mdir_->mid >= 0 && mdir_->rbyd.weight == 0) {
// TODO should we just make our pcache not assert?
// drop our pcache, we're not going to complete this commit
lfs_cache_zero(lfs, &lfs->pcache);
@@ -5497,22 +5440,22 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
//
// this pushes gstate up into the mroot when relocating, and
// helps avoid corner case issues when splitting/dropping
if (mdir->mid == LFSR_MID_MROOT
|| lfsr_mdir_cmp(mdir, msource) == 0) {
err = lfsr_rbyd_appendgdelta(lfs, &mdir->rbyd);
if (mdir_->mid == LFSR_MID_MROOT
|| lfsr_mdir_cmp(mdir_, mdir) == 0) {
err = lfsr_rbyd_appendgdelta(lfs, &mdir_->rbyd);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
} else {
// consume gstate so we don't lose any info
err = lfsr_fs_consumegdelta(lfs, mdir);
err = lfsr_fs_consumegdelta(lfs, mdir_);
if (err) {
return err;
}
}
err = lfsr_rbyd_commit(lfs, &mdir->rbyd, NULL, 0);
err = lfsr_rbyd_commit(lfs, &mdir_->rbyd, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -5520,8 +5463,8 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// TODO avoid duplicate conditions somehow?
// success? gstate is committed
if (mdir->mid == LFSR_MID_MROOT
|| lfsr_mdir_cmp(mdir, msource) == 0) {
if (mdir_->mid == LFSR_MID_MROOT
|| lfsr_mdir_cmp(mdir_, mdir) == 0) {
lfsr_fs_flushgdelta(lfs);
}
}
@@ -5531,11 +5474,17 @@ static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir,
// low-level mdir commit, does not handle mtree/mlist updates
static int lfsr_mdir_commit_(lfs_t *lfs, lfsr_mdir_t *mdir,
lfs_ssize_t start_id, lfs_ssize_t end_id,
lfs_size_t *split_id_,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// try to append a commit
lfsr_mdir_t mdir_ = *mdir;
int err = lfsr_rbyd_appendall(lfs, &mdir_.rbyd, -1, -1, attrs, attr_count);
// TODO handle this differently?
// TODO let the lower rbyd layer handle this somehow?
// mark mdir as unerased in case we fail
mdir->rbyd.off = lfs->cfg->block_size;
int err = lfsr_rbyd_appendall(lfs, &mdir_.rbyd, start_id, end_id,
attrs, attr_count);
if (err && err != LFS_ERR_RANGE) {
return err;
}
@@ -5586,7 +5535,7 @@ compact:;
// can't commit, try to compact
// check if we're within our compaction threshold
int fits = lfsr_rbyd_estimate(lfs, &mdir->rbyd, -1,
int fits = lfsr_rbyd_estimate(lfs, &mdir->rbyd, start_id, end_id,
lfs->cfg->block_size/2,
split_id_);
if (fits < 0) {
@@ -5599,7 +5548,7 @@ compact:;
}
// try to compact
err = lfsr_mdir_compact_(lfs, &mdir_, LFSR_MID_WL, -1, -1, mdir,
err = lfsr_mdir_compact_(lfs, &mdir_, LFSR_MID_WL, start_id, end_id, mdir,
attrs, attr_count,
NULL, 0);
if (err) {
@@ -5612,14 +5561,14 @@ compact:;
}
// low-level mdir split, note this is really an operation on the mtree
static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
lfsr_mdir_t *mdir, lfsr_mdir_t *msibling,
static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree_,
lfsr_mdir_t *mdir_, lfsr_mdir_t *msibling_,
lfs_ssize_t start_id, lfs_ssize_t end_id,
const lfsr_mdir_t *msource, lfs_size_t split_id,
const lfsr_mdir_t *mdir, lfs_size_t split_id,
const lfsr_attr_t *attrs, lfs_size_t attr_count) {
// if we're the mroot, create a new mtree, assume the upper layers
// will take care of grafting our mtree into the mroot as needed
lfs_ssize_t mid = msource->mid;
lfs_ssize_t mid = mdir->mid;
if (mid == LFSR_MID_MROOT) {
mid = 0;
@@ -5633,7 +5582,7 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
// 2. it makes it so we can actually split, lfsr_btree_split
// currently doesn't support an empty tree
//
int err = lfsr_btree_push(lfs, mtree, 0, LFSR_TAG_MDIR, 1,
int err = lfsr_btree_push(lfs, mtree_, 0, LFSR_TAG_MDIR, 1,
LFSR_DATA_NULL);
if (err) {
return err;
@@ -5645,26 +5594,26 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
// we do this here so we don't have to worry about corner cases
// with dropping mdirs during a split
} else {
int err = lfsr_fs_consumegdelta(lfs, mdir);
int err = lfsr_fs_consumegdelta(lfs, mdir_);
if (err) {
return err;
}
}
// we should have something to split here
LFS_ASSERT(split_id > 0 && split_id < msource->rbyd.weight);
LFS_ASSERT(split_id > 0 && split_id < mdir->rbyd.weight);
// compact into new mdir tags < split_id
int err = lfsr_mdir_compact_(lfs, mdir, mid, start_id, split_id,
msource, attrs, attr_count, NULL, 0);
int err = lfsr_mdir_compact_(lfs, mdir_, mid, start_id, split_id,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
// compact into new mdir tags >= split_id
err = lfsr_mdir_compact_(lfs, msibling, mid+1, split_id, end_id,
msource, attrs, attr_count, NULL, 0);
err = lfsr_mdir_compact_(lfs, msibling_, mid+1, split_id, end_id,
mdir, attrs, attr_count, NULL, 0);
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
@@ -5673,13 +5622,13 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
LFS_DEBUG("Splitting mdir 0x{%"PRIx32",%"PRIx32"} "
"-> 0x{%"PRIx32",%"PRIx32"}"
", 0x{%"PRIx32",%"PRIx32"}",
msource->rbyd.block, msource->redund_block,
mdir->rbyd.block, mdir->redund_block,
msibling->rbyd.block, msibling->redund_block);
mdir_->rbyd.block, mdir_->redund_block,
msibling_->rbyd.block, msibling_->redund_block);
// because of defered commits, both children can still be reduced
// to zero, need to catch this here
if (mdir->rbyd.weight > 0 && msibling->rbyd.weight > 0) {
if (mdir_->rbyd.weight > 0 && msibling_->rbyd.weight > 0) {
// update out mtree
// lookup first name in sibling to use as the split name
@@ -5688,7 +5637,7 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
// case they introduce a new name!
lfsr_tag_t stag;
lfsr_data_t sdata;
err = lfsr_mdir_lookupnext(lfs, msibling, 0, LFSR_TAG_NAME,
err = lfsr_mdir_lookupnext(lfs, msibling_, 0, LFSR_TAG_NAME,
NULL, &stag, &sdata);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
@@ -5696,17 +5645,17 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
}
uint8_t buf1[LFSR_MPTR_DSIZE];
lfs_ssize_t d1 = lfsr_mdir_todisk(lfs, mdir, buf1);
lfs_ssize_t d1 = lfsr_mdir_todisk(lfs, mdir_, buf1);
if (d1 < 0) {
return d1;
}
uint8_t buf2[LFSR_MPTR_DSIZE];
lfs_ssize_t d2 = lfsr_mdir_todisk(lfs, msibling, buf2);
lfs_ssize_t d2 = lfsr_mdir_todisk(lfs, msibling_, buf2);
if (d2 < 0) {
return d2;
}
err = lfsr_btree_split(lfs, mtree, mid,
err = lfsr_btree_split(lfs, mtree_, mid,
(lfsr_tag_suptype(stag) == LFSR_TAG_NAME
? sdata
: LFSR_DATA_NULL),
@@ -5717,36 +5666,36 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
}
// one sibling reduced to zero
} else if (mdir->rbyd.weight > 0) {
} else if (mdir_->rbyd.weight > 0) {
LFS_DEBUG("Dropping mdir 0x{%"PRIx32",%"PRIx32"}",
mdir->rbyd.block, mdir->redund_block);
mdir_->rbyd.block, mdir_->redund_block);
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, mdir, buf);
lfs_ssize_t d = lfsr_mdir_todisk(lfs, mdir_, buf);
if (d < 0) {
return d;
}
err = lfsr_btree_set(lfs, mtree, mid, LFSR_TAG_MDIR, 1,
err = lfsr_btree_set(lfs, mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
}
// other sibling reduced to zero
} else if (msibling->rbyd.weight > 0) {
} else if (msibling_->rbyd.weight > 0) {
LFS_DEBUG("Dropping mdir 0x{%"PRIx32",%"PRIx32"}",
msibling->rbyd.block, msibling->redund_block);
msibling_->rbyd.block, msibling_->redund_block);
// update our mtree
uint8_t buf[LFSR_MPTR_DSIZE];
lfs_ssize_t d = lfsr_mdir_todisk(lfs, msibling, buf);
lfs_ssize_t d = lfsr_mdir_todisk(lfs, msibling_, buf);
if (d < 0) {
return d;
}
err = lfsr_btree_set(lfs, mtree, mid, LFSR_TAG_MDIR, 1,
err = lfsr_btree_set(lfs, mtree_, mid, LFSR_TAG_MDIR, 1,
LFSR_DATA_BUF(buf, d));
if (err) {
return err;
@@ -5755,31 +5704,17 @@ static int lfsr_mtree_split_(lfs_t *lfs, lfsr_btree_t *mtree,
// both siblings reduced to zero
} else {
LFS_DEBUG("Dropping mdir 0x{%"PRIx32",%"PRIx32"}",
mdir->rbyd.block, mdir->redund_block);
mdir_->rbyd.block, mdir_->redund_block);
LFS_DEBUG("Dropping mdir 0x{%"PRIx32",%"PRIx32"}",
msibling->rbyd.block, msibling->redund_block);
msibling_->rbyd.block, msibling_->redund_block);
// update our mtree
err = lfsr_btree_pop(lfs, mtree, mid);
err = lfsr_btree_pop(lfs, mtree_, mid);
if (err) {
return err;
}
}
// if we split we need to fix our grm before we can propagate our commit
//
// If this feels a bit hacky, that's because it is. The way the grm
// interacts with mtree splits is not great.
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].tag == LFSR_TAG_GRM) {
int err = lfsr_grm_split(lfs, lfs->grmd, attrs[i].data,
msource->mid, mdir->rbyd.weight);
if (err) {
return err;
}
}
}
return 0;
}
@@ -5806,7 +5741,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
// attempt to commit/compact the mdir normally
lfsr_mdir_t mdir_ = *mdir;
lfs_size_t split_id;
int err = lfsr_mdir_commit_(lfs, &mdir_, &split_id, attrs, attr_count);
int err = lfsr_mdir_commit_(lfs, &mdir_, -1, -1, &split_id,
attrs, attr_count);
if (err && err != LFS_ERR_RANGE) {
return err;
}
@@ -5829,10 +5765,16 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
// attributes in mroot
LFS_ASSERT(lfsr_btree_weight(&mtree_) == 0);
// TODO wait, do we need to update lfs->mroot and mdir eagerly
// for the same reason?
//
// we need to update the mroot to track that a prog failed
mroot_ = mdir_;
// do we still need to split?
//
// note init_id is changed to 0 here, ignoring -1 attrs
int fits = lfsr_rbyd_estimate(lfs, &mdir->rbyd, 0,
int fits = lfsr_rbyd_estimate(lfs, &mdir->rbyd, 0, -1,
lfs->cfg->block_size/2,
&split_id);
if (fits < 0) {
@@ -5894,24 +5836,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
}
}
// commit mtree, also copying over any -1 tags
lfsr_tag_t tag;
uint8_t buf[LFSR_BTREE_DSIZE];
lfs_ssize_t d = lfsr_btree_todisk(lfs, &mtree_, LFSR_TAG_MTREE,
&tag, buf);
if (d < 0) {
return d;
}
err = lfsr_mdir_compact_(lfs, &mroot_, LFSR_MID_WL, -1, 0,
mdir, attrs, attr_count, LFSR_ATTRS(
LFSR_ATTR_(-1, tag, 0, buf, d)));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
return err;
}
dirtymroot = (lfsr_mdir_cmp(&lfs->mroot, &mroot_) != 0);
dirtymtree = true;
// splitting a normal mdir
} else {
@@ -5975,6 +5900,63 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
mroot_ = mdir_;
}
// before we continue we need to update our grm in case of splits/drops
//
// this gets pretty ugly
//
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].tag == LFSR_TAG_GRM) {
// to fix grms, we 1. decode, 2. fix, 3. reencode, 4. xor into
// any other pending grm delta
lfsr_grm_t grm;
lfs_ssize_t d = lfsr_grm_fromdisk(lfs, &grm, attrs[i].data);
if (d < 0) {
return d;
}
if (grm.mid == mdir->mid) {
LFS_ASSERT(grm.rid <= mdir->rbyd.weight);
// TODO do we need this if we allow mid=0 => mroot when inlined?
// update mid if we are uninlining
grm.mid = lfs_smax32(grm.mid, 0);
if (grm.rid >= mdir_.rbyd.weight) {
grm.mid += 1;
grm.rid -= mdir_.rbyd.weight;
}
// update mid if we had a split or drop
} else if (grm.mid > mdir->mid
&& lfsr_btree_weight(&mtree_) != lfsr_mtree_weight(lfs)) {
grm.mid += lfsr_btree_weight(&mtree_)
- lfsr_mtree_weight(lfs);
}
// make sure to zero so we don't end up with trailing garbage
uint8_t buf[LFSR_GRM_DSIZE];
memset(buf, 0, LFSR_GRM_DSIZE);
d = lfsr_grm_todisk(lfs, &grm, buf);
if (d < 0) {
return d;
}
// assume we already xored our gdelta with the grm, so we need to
// xor the grm out of the gdelta
//
// gd' = gd xor (grm' xor grm)
//
int err = lfsr_grm_xor(lfs, buf, attrs[i].data);
if (err) {
return err;
}
err = lfsr_grm_xor(lfs, lfs->grmd,
LFSR_DATA_BUF(buf, LFSR_GRM_DSIZE));
if (err) {
return err;
}
}
}
// need to update mtree?
if (dirtymtree) {
LFS_ASSERT(mdir_.mid != LFSR_MID_MROOT);
@@ -5988,7 +5970,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
return d;
}
err = lfsr_mdir_commit_(lfs, &mroot_, NULL, LFSR_ATTRS(
err = lfsr_mdir_commit_(lfs, &mroot_, -1, 0, NULL, LFSR_ATTRS(
LFSR_ATTR_(-1, tag, 0, buf, d)));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -6006,7 +5988,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
int err = lfsr_mtree_parent(lfs, lfsr_mdir_mptr(&mchildroot),
&mparentroot);
if (err && err != LFS_ERR_NOENT) {
return err;
return err;
}
if (err == LFS_ERR_NOENT) {
break;
@@ -6025,7 +6007,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
}
lfsr_mdir_t mparentroot_ = mparentroot;
err = lfsr_mdir_commit_(lfs, &mparentroot_, NULL, LFSR_ATTRS(
err = lfsr_mdir_commit_(lfs, &mparentroot_, -1, -1, NULL, LFSR_ATTRS(
LFSR_ATTR(-1, MROOT, 0, buf, d)));
if (err) {
LFS_ASSERT(err != LFS_ERR_RANGE);
@@ -6092,27 +6074,43 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
// update our gstate
for (lfs_size_t i = 0; i < attr_count; i++) {
if (attrs[i].tag == LFSR_TAG_GRM) {
// make sure to zero to avoid leaking anything
memset(lfs->grm, 0, LFSR_GRM_DSIZE);
lfs_ssize_t d = lfsr_data_read(lfs, attrs[i].data, 0,
lfs->grm, LFSR_GRM_DSIZE);
lfs_ssize_t d = lfsr_grm_fromdisk(lfs, &lfs->grm_, attrs[i].data);
if (d < 0) {
return d;
}
// TODO wait does this get triggered incorrectly if we drop?
// since weight' != weight?
// TODO use bool split?
// we need to fix our grm, again, if a split occured
//
// I mentioned grm/mtree didn't interact well didn't I?
//
if (lfsr_btree_weight(&mtree_) != lfsr_mtree_weight(lfs)) {
err = lfsr_grm_split(lfs, lfs->grm, attrs[i].data,
mdir->mid, mdir_.rbyd.weight);
if (err) {
return err;
// repeat the fix of our grm
if (lfs->grm_.mid == mdir->mid) {
LFS_ASSERT(lfs->grm_.rid <= mdir->rbyd.weight);
// TODO do we need this if we allow mid=0 => mroot when inlined?
// update mid if we are uninlining
lfs->grm_.mid = lfs_smax32(lfs->grm_.mid, 0);
if (lfs->grm_.rid >= mdir_.rbyd.weight) {
lfs->grm_.mid += 1;
lfs->grm_.rid -= mdir_.rbyd.weight;
}
// update mid if we had a split or drop
} else if (lfs->grm_.mid > mdir->mid
&& lfsr_btree_weight(&mtree_) != lfsr_mtree_weight(lfs)) {
lfs->grm_.mid += lfsr_btree_weight(&mtree_)
- lfsr_mtree_weight(lfs);
}
// TODO this is a big cludge, support for mid=0 when inlined?
// adjust mid if mtree is inlined
if (lfsr_btree_weight(&mtree_) == 0) {
LFS_ASSERT(lfs->grm_.mid <= 0);
if (lfs->grm_.mid == 0) {
lfs->grm_.mid = LFSR_MID_MROOT;
}
}
// keep track of the exact encoding on-disk
memset(lfs->grm, 0, LFSR_GRM_DSIZE);
d = lfsr_grm_todisk(lfs, &lfs->grm_, lfs->grm);
if (d < 0) {
return d;
}
}
}
@@ -6127,8 +6125,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
}
if (opened->mdir.mid == mdir->mid) {
LFS_ASSERT(opened->rid < (lfs_ssize_t)opened->mdir.rbyd.weight);
LFS_ASSERT(opened->rid != -1);
LFS_ASSERT(opened->rid < (lfs_ssize_t)mdir->rbyd.weight);
// first play out any attrs that change our rid
for (lfs_size_t i = 0; i < attr_count; i++) {
@@ -6147,7 +6144,7 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
// update mdir to follow rid
if (opened->rid == -2) {
// skip removed mdirs
} else if ((lfs_size_t)opened->rid >= mdir_.rbyd.weight) {
} else if (opened->rid >= (lfs_ssize_t)mdir_.rbyd.weight) {
LFS_ASSERT(lfsr_btree_weight(&mtree_)
!= lfsr_mtree_weight(lfs));
opened->rid = opened->rid - mdir_.rbyd.weight;
@@ -7313,7 +7310,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) {
//
uint8_t buf[LFSR_GRM_DSIZE];
lfs_ssize_t d = lfsr_grm_todisk(lfs,
&(lfsr_grm_t){.mid=lfs_smax32(mdir.mid, 0), .rid=rid},
&(lfsr_grm_t){.mid=mdir.mid, .rid=rid},
buf);
if (d < 0) {
return d;
@@ -7346,6 +7343,117 @@ failed_with_parent:
return err;
}
int lfsr_remove(lfs_t *lfs, const char *path) {
// prepare our filesystem for writing
int err= lfsr_fs_preparemutation(lfs);
if (err) {
return err;
}
// lookup our entry
lfsr_mdir_t mdir;
lfs_ssize_t rid;
lfsr_tag_t tag;
err = lfsr_mtree_pathlookup(lfs, path,
&mdir, &rid, &tag,
NULL, NULL, 0);
if (err) {
return err;
}
// if we're removing a directory, we need to also remove the
// dstart entry, first lets figure out the did
uint8_t grm_buf[LFSR_GRM_DSIZE];
lfs_ssize_t grm_d;
if (tag == LFSR_TAG_DIR) {
lfsr_data_t data;
int err = lfsr_mdir_lookup(lfs, &mdir, rid, LFSR_TAG_DID,
NULL, &data);
if (err) {
return err;
}
lfs_size_t did;
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &did);
if (d < 0) {
return d;
}
// check that the directory is empty
lfsr_mdir_t dstart_mdir;
lfs_ssize_t dstart_rid;
err = lfsr_mtree_dnamelookup(lfs, did, NULL, 0,
&dstart_mdir, &dstart_rid, NULL, NULL);
if (err) {
LFS_ASSERT(err != LFS_ERR_NOENT);
return err;
}
lfsr_mdir_t mdir_ = dstart_mdir;
lfs_ssize_t rid_ = dstart_rid + 1;
if (rid_ >= (lfs_ssize_t)mdir_.rbyd.weight) {
// out of mdirs?
lfs_ssize_t mid = mdir_.mid + 1;
if (mid >= (lfs_ssize_t)lfsr_mtree_weight(lfs)) {
goto empty;
}
int err = lfsr_mtree_lookup(lfs, mid, &mdir_);
if (err) {
return err;
}
rid_ = 0;
}
lfsr_tag_t tag_;
err = lfsr_mdir_lookup(lfs, &mdir_, rid_, LFSR_TAG_WIDENAME,
&tag_, NULL);
if (err) {
return err;
}
if (tag_ != LFSR_TAG_DSTART) {
return LFS_ERR_NOTEMPTY;
}
empty:;
// TODO should we just make this an atomic remove?
// adjust rid if grm is on the same mdir as our dir
if (dstart_mdir.mid == mdir.mid && dstart_rid > rid) {
dstart_rid -= 1;
}
// create a grm to remove the dstart entry
grm_d = lfsr_grm_todisk(lfs,
&(lfsr_grm_t){.mid=dstart_mdir.mid, .rid=dstart_rid},
grm_buf);
if (grm_d < 0) {
return grm_d;
}
}
// remove the metadata entry
err = lfsr_mdir_commit(lfs, &mdir, &rid, LFSR_ATTRS(
LFSR_ATTR(rid, UNR, -1, NULL, 0),
(tag == LFSR_TAG_DIR
? LFSR_ATTR(-1, GRM, 0, grm_buf, grm_d)
: LFSR_ATTR_NOOP)));
if (err) {
return err;
}
// if we were a directory, we need to clean up, fortunately we can leave
// this up to lfsr_fs_fixgrm
if (tag == LFSR_TAG_DIR) {
err = lfsr_fs_fixgrm(lfs);
if (err) {
return err;
}
}
return 0;
}
int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
// lookup our directory
lfsr_mdir_t mdir;
+1
View File
@@ -549,6 +549,7 @@ int lfsr_unmount(lfs_t *lfs);
// If removing a directory, the directory must be empty.
// Returns a negative error code on failure.
int lfs_remove(lfs_t *lfs, const char *path);
int lfsr_remove(lfs_t *lfs, const char *path);
#endif
#ifndef LFS_READONLY
+1001 -4
View File
File diff suppressed because it is too large Load Diff