diff --git a/lfs.c b/lfs.c index 9c409770..be176b3d 100644 --- a/lfs.c +++ b/lfs.c @@ -724,10 +724,6 @@ static inline bool lfsr_tag_istrunk(lfsr_tag_t tag) { return (tag & 0x6000) != 0x2000; } -static inline uint8_t lfsr_tag_filetype(lfsr_tag_t tag) { - return tag - LFSR_TAG_REG; -} - static inline bool lfsr_tag_isinternal(lfsr_tag_t tag) { return tag & LFSR_TAG_INTERNAL; } @@ -3017,11 +3013,6 @@ static int lfsr_rbyd_appendcompactattr(lfs_t *lfs, lfsr_rbyd_t *rbyd, return 0; } -// lfsr_rbyd_appendcompactrbyd actually calls lfsr_rbyd_compact if it -// encounters an inlined tree -static int lfsr_rbyd_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd, - bool deferred, lfs_off_t off); - static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_, lfsr_srid_t start_rid, lfsr_srid_t end_rid, const lfsr_rbyd_t *rbyd) { @@ -3047,69 +3038,11 @@ static int lfsr_rbyd_appendcompactrbyd(lfs_t *lfs, lfsr_rbyd_t *rbyd_, break; } - // found an inlined tree? we need to compact the tree as well to bring - // it along with us - if (tag == LFSR_TAG_TRUNK) { - lfsr_rbyd_t inlined_rbyd; - err = lfsr_data_readtrunk(lfs, &data, &inlined_rbyd); - if (err) { - return err; - } - inlined_rbyd.block = rbyd->block; - - // keep track of the start of our new tree - lfs_size_t off = rbyd_->eoff; - - // what a great use case for recursion! too bad we can't use - // recursion here - lfsr_srid_t rid_ = -1; - lfsr_tag_t tag_ = 0; - while (true) { - lfsr_rid_t weight_; - lfsr_data_t data_; - err = lfsr_rbyd_lookupnext(lfs, &inlined_rbyd, - rid_, tag_+1, - &rid_, &tag_, &weight_, &data_); - if (err) { - if (err == LFS_ERR_NOENT) { - break; - } - return err; - } - - // write the tag_ - err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, - tag_, weight_, data_); - if (err) { - LFS_ASSERT(err != LFS_ERR_RANGE); - return err; - } - } - - // compact our inlined tree - err = lfsr_rbyd_compact(lfs, rbyd_, true, off); - if (err) { - LFS_ASSERT(err != LFS_ERR_RANGE); - return err; - } - - // write the new inlined tree tag - uint8_t trunk_buf[LFSR_TRUNK_DSIZE]; - err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, - tag, weight, lfsr_data_fromtrunk(lfs, - rbyd_, trunk_buf)); - if (err) { - LFS_ASSERT(err != LFS_ERR_RANGE); - return err; - } - - } else { - // write the tag - err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data); - if (err) { - LFS_ASSERT(err != LFS_ERR_RANGE); - return err; - } + // write the tag + err = lfsr_rbyd_appendcompactattr(lfs, rbyd_, tag, weight, data); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; } } @@ -3120,8 +3053,9 @@ static int lfsr_rbyd_compact(lfs_t *lfs, lfsr_rbyd_t *rbyd, bool deferred, lfs_size_t off) { // must fetch before mutating! LFS_ASSERT(lfsr_rbyd_isfetched(rbyd)); + // offset must be after the revision count + LFS_ASSERT(off >= sizeof(uint32_t)); - // TODO still need this? // ignore empty rbyds, we can't really compact these, so leave it up to // upper layers to deal with this if (rbyd->eoff <= sizeof(uint32_t)) { @@ -4719,13 +4653,15 @@ static int lfsr_data_readmblocks(lfs_t *lfs, lfsr_data_t *data, // track opened mdirs that may need to by updated static void lfsr_mdir_addopened(lfs_t *lfs, int type, lfsr_openedmdir_t *opened) { - opened->next = lfs->opened[type]; - lfs->opened[type] = opened; + opened->next = lfs->opened[type-LFS_TYPE_REG]; + lfs->opened[type-LFS_TYPE_REG] = opened; } static void lfsr_mdir_removeopened(lfs_t *lfs, int type, lfsr_openedmdir_t *opened) { - for (lfsr_openedmdir_t **p = &lfs->opened[type]; *p; p = &(*p)->next) { + for (lfsr_openedmdir_t **p = &lfs->opened[type-LFS_TYPE_REG]; + *p; + p = &(*p)->next) { if (*p == opened) { *p = (*p)->next; break; @@ -4735,7 +4671,9 @@ static void lfsr_mdir_removeopened(lfs_t *lfs, int type, static bool lfsr_mdir_isopened(lfs_t *lfs, int type, const lfsr_openedmdir_t *opened) { - for (lfsr_openedmdir_t *p = lfs->opened[type]; p; p = p->next) { + for (lfsr_openedmdir_t *p = lfs->opened[type-LFS_TYPE_REG]; + p; + p = p->next) { if (p == opened) { return true; } @@ -5148,13 +5086,14 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir, } else if (attrs[i].tag == LFSR_TAG_DEFER) { const lfsr_defer_t *defer = (const lfsr_defer_t*)attrs[i].data.u.b.buffer; + // swap out our trunk/weight temporarily, note we're operating // on a copy so if this fails not _too_ many things will get // messed up // // it is important that these rbyds share eoff/cksum/etc - mdir_.u.m.weight = defer->rbyd->weight; mdir_.u.m.trunk = defer->rbyd->trunk; + mdir_.u.m.weight = defer->rbyd->weight; // append any deferred attributes int err = lfsr_rbyd_appendattrs(lfs, &mdir_.u.r.rbyd, -1, -1, @@ -5163,10 +5102,11 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir, return err; } - defer->rbyd->weight = mdir_.u.m.weight; + // revert to mdir trunk/weight defer->rbyd->trunk = mdir_.u.m.trunk; - mdir_.u.m.weight = mdir->u.m.weight; + defer->rbyd->weight = mdir_.u.m.weight; mdir_.u.m.trunk = mdir->u.m.trunk; + mdir_.u.m.weight = mdir->u.m.weight; // write out normal tags normally } else { @@ -5228,18 +5168,207 @@ static int lfsr_mdir_commit__(lfs_t *lfs, lfsr_mdir_t *mdir, return 0; } +// needed by lfsr_mdir_compact__ +static bool lfsr_file_isinlineddata(const lfsr_file_t *file); +static bool lfsr_file_isinlinedtree(const lfsr_file_t *file); + static int lfsr_mdir_compact__(lfs_t *lfs, lfsr_mdir_t *mdir_, lfsr_srid_t start_rid, lfsr_srid_t end_rid, const lfsr_mdir_t *mdir) { - int err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd, - start_rid, end_rid, &mdir->u.r.rbyd); + // this is basically the same as lfsr_rbyd_appendcompactrbyd + + // lfsr_rbyd_compact, but with special handling for inlined trees. + // + // it's really tempting to deduplicate this via recursion! but we can't + // do that here + + // copy over tags in the rbyd in order + lfsr_srid_t rid = start_rid; + lfsr_tag_t tag = 0; + while (true) { + lfsr_rid_t weight; + lfsr_data_t data; + int err = lfsr_rbyd_lookupnext(lfs, &mdir->u.r.rbyd, + rid, tag+1, + &rid, &tag, &weight, &data); + if (err) { + if (err == LFS_ERR_NOENT) { + break; + } + return err; + } + // end of range? note the use of rid+1 and unsigned comparison here to + // treat end_rid=-1 as "unbounded" in such a way that rid=-1 is still + // included + if ((lfs_size_t)(rid + 1) > (lfs_size_t)end_rid) { + break; + } + + // found an inlined data? we can just copy this like normal but + // we need to update any opened inlined files + if (tag == LFSR_TAG_INLINED) { + // write the tag + err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd, + tag, weight, data); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + // stage any opened deferred trees with their new location so + // we can update these later if our commit is a success + for (lfsr_openedmdir_t *opened = lfs->opened[ + LFS_TYPE_REG-LFS_TYPE_REG]; + opened; + opened = opened->next) { + lfsr_file_t *file = (lfsr_file_t*)opened; + if (lfsr_file_isinlineddata(file) + && file->inlined.u.data.u.d.block == data.u.d.block + && file->inlined.u.data.u.d.off == data.u.d.off) { + // this is a bit tricky see we don't know the tag size, + // but we have enough enough info + file->inlined_.u.data = LFSR_DATA_DISK( + mdir_->u.r.rbyd.block, + mdir_->u.r.rbyd.eoff + - lfsr_data_size(&file->inlined.u.data), + lfsr_data_size(&file->inlined.u.data)); + } + } + + // found an inlined tree? we need to compact the tree as well to bring + // it along with us + } else if (tag == LFSR_TAG_TRUNK) { + lfsr_rbyd_t inlined_rbyd; + err = lfsr_data_readtrunk(lfs, &data, &inlined_rbyd); + if (err) { + return err; + } + inlined_rbyd.block = mdir->u.r.rbyd.block; + + // keep track of the start of our new tree + lfs_size_t off = mdir_->u.r.rbyd.eoff; + + // compact our inlined tree + err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd, -1, -1, + &inlined_rbyd); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, true, off); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + // write the new inlined tree tag + uint8_t trunk_buf[LFSR_TRUNK_DSIZE]; + err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd, + tag, weight, lfsr_data_fromtrunk(lfs, + &mdir_->u.r.rbyd, trunk_buf)); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + // stage any opened deferred trees with their new location so + // we can update these later if our commit is a success + for (lfsr_openedmdir_t *opened = lfs->opened[ + LFS_TYPE_REG-LFS_TYPE_REG]; + opened; + opened = opened->next) { + lfsr_file_t *file = (lfsr_file_t*)opened; + if (lfsr_file_isinlinedtree(file) + && file->inlined.u.rbyd.block == mdir->u.r.rbyd.block + && file->inlined.u.rbyd.trunk == inlined_rbyd.trunk) { + file->inlined_.u.rbyd.block = mdir_->u.r.rbyd.block; + file->inlined_.u.rbyd.trunk = mdir_->u.r.rbyd.trunk; + file->inlined_.u.rbyd.weight = mdir_->u.r.rbyd.weight; + file->inlined_.u.deferred.overhead + = file->inlined.u.deferred.overhead; + } + } + + } else { + // write the tag + err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd, + tag, weight, data); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + } + } + + int err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, false, sizeof(uint32_t)); if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); return err; } - err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, false, sizeof(uint32_t)); - if (err) { - return err; + // we're not quite done! we also need to bring over any opened, + // unsynced files + // + // TODO note for this to fully work we need to mark opened readonly + // files as unsynced if their entry is updated + for (lfsr_openedmdir_t *opened = lfs->opened[ + LFS_TYPE_REG-LFS_TYPE_REG]; + opened; + opened = opened->next) { + lfsr_file_t *file = (lfsr_file_t*)opened; + // inlined data? + if (lfsr_file_isinlineddata(file) + && (file->flags & LFS_F_UNSYNCED) + && file->inlined.u.data.u.d.block == mdir->u.r.rbyd.block) { + // write the data as a deferred tag + err = lfsr_rbyd_appendcompactattr(lfs, &mdir_->u.r.rbyd, + LFSR_TAG_DEFERRED(INLINED), 0, file->inlined.u.data); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + // this is a bit tricky see we don't know the tag size, + // but we have enough enough info + file->inlined_.u.data = LFSR_DATA_DISK( + mdir_->u.r.rbyd.block, + mdir_->u.r.rbyd.eoff + - lfsr_data_size(&file->inlined.u.data), + lfsr_data_size(&file->inlined.u.data)); + + // inlined tree? + } else if (lfsr_file_isinlinedtree(file) + && (file->flags & LFS_F_UNSYNCED) + && file->inlined.u.rbyd.block == mdir->u.r.rbyd.block) { + // save our current off/trunk/weight + lfs_size_t off = mdir_->u.r.rbyd.eoff; + lfs_size_t trunk = mdir_->u.r.rbyd.trunk; + lfsr_srid_t weight = mdir_->u.r.rbyd.weight; + + // compact our inlined tree + err = lfsr_rbyd_appendcompactrbyd(lfs, &mdir_->u.r.rbyd, -1, -1, + &file->inlined.u.rbyd); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + err = lfsr_rbyd_compact(lfs, &mdir_->u.r.rbyd, true, off); + if (err) { + LFS_ASSERT(err != LFS_ERR_RANGE); + return err; + } + + // stage our new trunk and revert to mdir trunk/weight + file->inlined_.u.rbyd.block = mdir_->u.r.rbyd.block; + file->inlined_.u.rbyd.trunk = mdir_->u.r.rbyd.trunk; + file->inlined_.u.rbyd.weight = mdir_->u.r.rbyd.weight; + file->inlined_.u.deferred.overhead + = file->inlined.u.deferred.overhead; + + mdir_->u.r.rbyd.trunk = trunk; + mdir_->u.r.rbyd.weight = weight; + } } return 0; @@ -5347,8 +5476,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, if (mid == -1 || lfsr_mtree_isinlined(lfs)) { lfsr_mdir_unerase(&lfs->mroot); } - for (int type = 0; type < 2; type++) { - for (lfsr_openedmdir_t *opened = lfs->opened[type]; + for (int type = LFS_TYPE_REG; type < LFS_TYPE_REG+3; type++) { + for (lfsr_openedmdir_t *opened = lfs->opened[type-LFS_TYPE_REG]; opened; opened = opened->next) { if ((opened->mdir.mid & lfsr_midbmask(lfs)) @@ -5754,8 +5883,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, // gstate must have been committed by a lower-level function at this point LFS_ASSERT(lfsr_grm_iszero(lfs->dgrm)); - // update our gstate for (lfs_size_t i = 0; i < attr_count; i++) { + // update gstate if (attrs[i].tag == LFSR_TAG_GRM) { lfs->grm = *(lfsr_grm_t*)attrs[i].data.u.b.buffer; @@ -5765,8 +5894,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, } // update any opened mdirs - for (int type = 0; type < 2; type++) { - for (lfsr_openedmdir_t *opened = lfs->opened[type]; + for (int type = LFS_TYPE_REG; type < LFS_TYPE_REG+3; type++) { + for (lfsr_openedmdir_t *opened = lfs->opened[type-LFS_TYPE_REG]; opened; opened = opened->next) { // avoid double updating current mdir, and avoid updating @@ -5826,7 +5955,8 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, - lfsr_mtree_weight(lfs); } - // some extra work is needed for opened dirs + // update any changes to directory bookmarks/positions, this + // gets a bit tricky if (type == LFS_TYPE_DIR) { lfsr_dir_t *dir = (lfsr_dir_t*)opened; for (lfs_size_t i = 0; i < attr_count; i++) { @@ -5867,6 +5997,20 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, - lfsr_mtree_weight(lfs); } } + + // if we compacted, update any staged changes to inlined data/trees + if (type == LFS_TYPE_REG) { + lfsr_file_t *file = (lfsr_file_t*)opened; + if (mdir_.u.m.blocks[0] != mdir->u.m.blocks[0] + && ((lfsr_file_isinlineddata(file) + && file->inlined.u.data.u.d.block + == mdir->u.m.blocks[0]) + || (lfsr_file_isinlinedtree(file) + && file->inlined.u.rbyd.block + == mdir->u.m.blocks[0]))) { + file->inlined = file->inlined_; + } + } next:; } } @@ -7218,7 +7362,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { // the bookmark first risks inserting the bookmark before the metadata // entry, which breaks things. // - lfsr_mdir_addopened(lfs, LFS_TYPE_REG, &bookmark); + lfsr_mdir_addopened(lfs, LFS_TYPE_INTERNAL, &bookmark); // commit our new directory into our parent, creating a grm to self-remove // in case of powerloss @@ -7230,7 +7374,7 @@ int lfsr_mkdir(lfs_t *lfs, const char *path) { goto failed_with_bookmark; } - lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, &bookmark); + lfsr_mdir_removeopened(lfs, LFS_TYPE_INTERNAL, &bookmark); // commit our bookmark and zero the grm, the bookmark tag is an empty // entry that marks our did as allocated @@ -7494,7 +7638,7 @@ static int lfsr_mdir_stat(lfs_t *lfs, lfsr_mdir_t *mdir, lfsr_mid_t mid, } // get file type from the tag - info->type = lfsr_tag_filetype(tag); + info->type = lfsr_tag_subtype(tag); // get file name from the name entry LFS_ASSERT(lfsr_data_size(&data) <= LFS_NAME_MAX); @@ -7724,7 +7868,8 @@ int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir) { #define LFSR_FILE_INLINEDDATA 0x80000000 static bool lfsr_file_isinlineddata(const lfsr_file_t *file) { - return file->inlined.u.weight & LFSR_FILE_INLINEDDATA; + // this checks that both the inlineddata bit and non-zero + return (lfs_size_t)file->inlined.u.weight > (LFSR_FILE_INLINEDDATA | 0); } static bool lfsr_file_isinlinedtree(const lfsr_file_t *file) { @@ -7759,9 +7904,9 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, file->cfg = cfg; file->pos = 0; // default inlined state - file->inlined.u.d.block = 0; - file->inlined.u.d.off = 0; - file->inlined.u.d.size = LFSR_FILE_INLINEDDATA | 0; + file->inlined.u.data.u.d.block = 0; + file->inlined.u.data.u.d.off = 0; + file->inlined.u.data.u.d.size = LFSR_FILE_INLINEDDATA | 0; // lookup our parent lfsr_tag_t tag; @@ -7840,10 +7985,12 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, if (err != LFS_ERR_NOENT) { LFS_ASSERT(lfsr_file_inlinedsize(file) == 0); err = lfsr_data_readtrunk(lfs, &data, - (lfsr_rbyd_t*)&file->inlined); + &file->inlined.u.rbyd); if (err) { return err; } + + file->inlined.u.rbyd.block = file->m.mdir.u.m.blocks[0]; } } } @@ -7941,16 +8088,11 @@ int lfsr_file_read_(lfs_t *lfs, const lfsr_file_t *file, // is the data in an inlined tree? if (lfsr_file_isinlinedtree(file) && pos < lfsr_file_inlinedsize(file)) { - lfsr_rbyd_t rbyd; - rbyd.block = file->m.mdir.u.m.blocks[0]; - rbyd.trunk = file->inlined.u.t.trunk; - rbyd.weight = file->inlined.u.t.weight; - lfsr_srid_t rid; lfsr_tag_t tag; lfsr_rid_t weight; lfsr_data_t data; - int err = lfsr_rbyd_lookupnext(lfs, &rbyd, pos, 0, + int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd, pos, 0, &rid, &tag, &weight, &data); if (err && err != LFS_ERR_NOENT) { return err; @@ -8003,7 +8145,7 @@ lfs_ssize_t lfsr_file_read(lfs_t *lfs, lfsr_file_t *file, static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { while (file->buffer_size > 0) { // do we need an inlined tree? - if (lfsr_file_isinlineddata(file)) { + if (!lfsr_file_isinlinedtree(file)) { // TODO rm? we haven't updated file->size yet! // // we shouldn't reach this point if we still fit entirely // // in a simple inlined file @@ -8013,30 +8155,33 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { // do we carve out any data from the inlined data? lfsr_data_t left_data = LFSR_DATA_DISK( - file->inlined.u.d.block, - file->inlined.u.d.off, + file->inlined.u.data.u.d.block, + file->inlined.u.data.u.d.off, lfs_min32( file->buffer_pos, lfsr_file_inlinedsize(file))); lfs_off_t right_pos = file->buffer_pos + file->buffer_size; lfsr_data_t right_data = LFSR_DATA_DISK( - file->inlined.u.d.block, - file->inlined.u.d.off + right_pos, + file->inlined.u.data.u.d.block, + file->inlined.u.data.u.d.off + right_pos, lfsr_file_inlinedsize(file) - lfs_min32( right_pos, lfsr_file_inlinedsize(file))); // create a zero weight trunk - file->inlined.u.t.trunk = 0; - file->inlined.u.t.weight = 0; + // + // make sure to use our staging rbyd so we catch in-flight updates + // caused by mdir compactions + file->inlined_.u.rbyd.block = file->m.mdir.u.m.blocks[0]; + file->inlined_.u.rbyd.trunk = 0; + file->inlined_.u.rbyd.weight = 0; // TODO - file->inlined.u.t.overhead = 0; + file->inlined_.u.deferred.overhead = 0; int err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS( LFSR_ATTR_(file->m.mdir.mid, DEFER, 0, DEFER( - // TODO bit of a hack... - (lfsr_rbyd_t*)&file->inlined, + &file->inlined_.u.rbyd, // note we always need a zero entry (file->buffer_pos > 0 ? LFSR_ATTR(0, @@ -8058,6 +8203,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { return err; } + file->inlined = file->inlined_; file->buffer_size = 0; continue; } @@ -8069,19 +8215,12 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { // this is a complex question since we may be carving out leaves of the // inlined tree, we need to find these leaves to know for sure // - // TODO can we somehow fit an rbyd struct in the file_t so we don't - // need this copy? - lfsr_rbyd_t rbyd; - rbyd.block = file->m.mdir.u.m.blocks[0]; - rbyd.trunk = file->inlined.u.t.trunk; - rbyd.weight = file->inlined.u.t.weight; - lfsr_srid_t left_rid; lfsr_tag_t left_tag; lfsr_rid_t left_weight; lfsr_data_t left_data; - int err = lfsr_rbyd_lookupnext(lfs, &rbyd, - lfs_min32(file->buffer_pos, rbyd.weight-1), 0, + int err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd, + lfs_min32(file->buffer_pos, file->inlined.u.rbyd.weight-1), 0, &left_rid, &left_tag, &left_weight, &left_data); if (err && err != LFS_ERR_NOENT) { return err; @@ -8108,7 +8247,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { lfsr_tag_t right_tag; lfsr_rid_t right_weight; lfsr_data_t right_data; - err = lfsr_rbyd_lookupnext(lfs, &rbyd, + err = lfsr_rbyd_lookupnext(lfs, &file->inlined.u.rbyd, file->buffer_pos + file->buffer_size-1, 0, &right_rid, &right_tag, &right_weight, &right_data); if (err && err != LFS_ERR_NOENT) { @@ -8136,19 +8275,22 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { } // write out our buffer and any carved data + // + // make sure to use our staging rbyd so we catch in-flight updates + // caused by mdir compactions + file->inlined_ = file->inlined; err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS( LFSR_ATTR_(file->m.mdir.mid, DEFER, 0, DEFER( - // TODO bit of a hack... - (lfsr_rbyd_t*)&file->inlined, + &file->inlined_.u.rbyd, // first remove anything in the way LFSR_ATTR( lfs_min32( right_pos + lfsr_data_size(&right_data), - rbyd.weight)-1, + file->inlined.u.rbyd.weight)-1, RM, lfs_min32( right_pos + lfsr_data_size(&right_data), - rbyd.weight) - left_pos, + file->inlined.u.rbyd.weight) - left_pos, NULL), // TODO this should handling holes somehow (lfsr_data_size(&left_data) > 0 @@ -8171,6 +8313,7 @@ static int lfsr_file_flushbuffer(lfs_t *lfs, lfsr_file_t *file) { return err; } + file->inlined = file->inlined_; file->buffer_size = 0; continue; @@ -8258,12 +8401,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { return 0; } - // write out any data we need to - int err = lfsr_file_flush(lfs, file); - if (err) { - goto failed; - } - + int err; if (file->flags & LFS_F_UNSYNCED) { // TODO what if buffer_size > inlined_size? // TODO should we also update file to be unbuffered after syncing @@ -8288,7 +8426,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { } // but clear buffer after syncing simple inlined files, otherwise - // we risk O(n^2) behavior + // we risk runaway O(n^2) behavior file->buffer_size = 0; // we need to look up the inlined data again... @@ -8315,8 +8453,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { uint8_t trunk_buf[LFSR_TRUNK_DSIZE]; err = lfsr_mdir_commit(lfs, &file->m.mdir, LFSR_ATTRS( LFSR_ATTR(file->m.mdir.mid, WIDE(TRUNK), 0, FROMTRUNK( - // TODO too much of a hack? - lfs, (const lfsr_rbyd_t*)&file->inlined, trunk_buf)))); + lfs, &file->inlined.u.rbyd, trunk_buf)))); if (err) { goto failed; } @@ -11831,8 +11968,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { lfs->mleaf_bits = lfs_nlog2(lfs->cfg->block_size/16); // zero linked-lists of opened mdirs - lfs->opened[LFS_TYPE_REG] = NULL; - lfs->opened[LFS_TYPE_DIR] = NULL; + lfs->opened[LFS_TYPE_REG - LFS_TYPE_REG] = NULL; + lfs->opened[LFS_TYPE_DIR - LFS_TYPE_REG] = NULL; + lfs->opened[LFS_TYPE_INTERNAL - LFS_TYPE_REG] = NULL; // zero gstate memset(lfs->ggrm, 0, LFSR_GRM_DSIZE); diff --git a/lfs.h b/lfs.h index 9a88659f..946e5a79 100644 --- a/lfs.h +++ b/lfs.h @@ -106,8 +106,11 @@ enum lfs_error { // File types enum lfs_type { // file types - LFS_TYPE_REG = 0, - LFS_TYPE_DIR = 1, + LFS_TYPE_REG = 2, + LFS_TYPE_DIR = 3, + + // used internally, don't use this + LFS_TYPE_INTERNAL = 4, // // internally used types // LFS_TYPE_SPLICE = 0x400, @@ -152,6 +155,7 @@ enum lfs_open_flags { #endif // internally used flags + // TODO do we need both unflushed and unsynced? LFS_F_UNFLUSHED = 0x010000, // File's has data that needs to be written LFS_F_UNSYNCED = 0x020000, // File's metadata does not match storage LFS_F_ERRORED = 0x040000, // An error occurred during write @@ -490,6 +494,22 @@ typedef struct lfs_file { const struct lfs_file_config *cfg; } lfs_file_t; +typedef struct lfsr_inlined { + union { + // note sign bit indicates if data is a single inlined data, or an + // inlined tree, this works because inlined data is always on disk, + // so data.size always has sign=1 + lfs_soff_t weight; + lfsr_data_t data; + lfsr_rbyd_t rbyd; + struct { + lfs_soff_t weight; + lfs_size_t trunk; + lfs_size_t overhead; + } deferred; + } u; +} lfsr_inlined_t; + typedef struct lfsr_file { lfsr_openedmdir_t m; uint32_t flags; @@ -500,25 +520,11 @@ typedef struct lfsr_file { uint8_t *buffer; lfs_size_t buffer_size; - struct { - union { - // note sign bit indicates if data is a single inlined data, or an - // inlined tree, this works because inlined data is always on disk, - // so data.size always has sign=1 - lfs_soff_t weight; - lfsr_data_t data; - struct { - lfs_ssize_t size; - lfs_size_t off; - lfs_block_t block; - } d; - struct { - lfs_soff_t weight; - lfs_size_t trunk; - lfs_size_t overhead; - } t; - } u; - } inlined; + // we need copies of inlined references in case of mdir compaction, this + // exists here instead of on the stack becuase we don't know how many + // inlined files may be opened + lfsr_inlined_t inlined; + lfsr_inlined_t inlined_; const struct lfs_file_config *cfg; } lfsr_file_t; @@ -578,7 +584,7 @@ typedef struct lfs { // linked-lists of opened mdirs, we keep a separate linked-list // for each type since these need to be handled a bit differently - lfsr_openedmdir_t *opened[2]; + lfsr_openedmdir_t *opened[3]; #ifdef LFS_MIGRATE struct lfs1 *lfs1; diff --git a/scripts/dbglfs.py b/scripts/dbglfs.py index e8cb95e9..8ac9671a 100755 --- a/scripts/dbglfs.py +++ b/scripts/dbglfs.py @@ -874,6 +874,7 @@ def frepr(mdir, rid, tag): did, _ = fromleb128(data) did = '0x%x' % did return 'bookmark %s' % did + elif tag == TAG_DIR: # read the did did = '?' @@ -882,6 +883,25 @@ def frepr(mdir, rid, tag): did, _ = fromleb128(data) did = '0x%x' % did return 'dir %s' % did + + elif tag == TAG_REG: + size = 0 + structs = [] + # inlined? + done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_INLINED) + if not done and rid_ == rid and tag_ == TAG_INLINED: + size = max(size, len(data)) + structs.append('inlined 0x%x.%x %d' % (mdir.block, j+d, len(data))) + # inlined tree? + done, rid_, tag_, w_, j, d, data, _ = mdir.lookup(rid, TAG_TRUNK) + if not done and rid_ == rid and tag_ == TAG_TRUNK: + d = 0 + trunk, d_ = fromleb128(data[d:]); d += d_ + weight, d_ = fromleb128(data[d:]); d += d_ + size = max(size, weight) + structs.append('trunk 0x%x.%x' % (mdir.block, trunk)) + return 'reg %s' % ', '.join(it.chain(['%d' % size], structs)) + else: return 'type 0x%02x' % (tag & 0xff) diff --git a/tests/test_mtree.toml b/tests/test_mtree.toml index 0110879b..81f77e55 100644 --- a/tests/test_mtree.toml +++ b/tests/test_mtree.toml @@ -2707,8 +2707,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // insert a new entry, this should update our neighbors lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( @@ -2730,8 +2730,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m, sizeof(lfs.mroot.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -2757,8 +2757,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // try removing our left entry lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( @@ -2773,8 +2773,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m, sizeof(lfs.mroot.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -2800,8 +2800,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // try removing our left entry lfsr_mdir_commit(&lfs, &lfs.mroot, LFSR_ATTRS( @@ -2816,8 +2816,8 @@ code = ''' sizeof(lfs.mroot.u.m)) == 0); assert(right_neighbor.mdir.mid == -1); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -2845,8 +2845,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // prepare mroot with a large attr so the next entry can not fit uint8_t buffer[SIZE]; @@ -2895,8 +2895,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m, sizeof(msibling.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -2924,8 +2924,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // create 2 large entries that needs to be uninlined and split uint8_t buffer[SIZE]; @@ -2969,8 +2969,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m, sizeof(msibling.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -3025,8 +3025,8 @@ code = ''' .mdir={.mid=mdir.mid+0, .u.m=mdir.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=mdir.mid+2, .u.m=mdir.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // now add another large entry to the mdir, forcing a split memset(buffer, 'e', SIZE); @@ -3070,8 +3070,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &msibling.u.m, sizeof(msibling.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -3101,8 +3101,8 @@ code = ''' .mdir={.mid=0, .u.m=lfs.mroot.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=1, .u.m=lfs.mroot.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // prepare mroot with an attr uint8_t buffer[SIZE]; @@ -3136,8 +3136,8 @@ code = ''' assert(memcmp(&right_neighbor.mdir.u.m, &lfs.mroot.u.m, sizeof(lfs.mroot.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -3194,8 +3194,8 @@ code = ''' .mdir={.mid=mdir.mid+0, .u.m=mdir.u.m}}; lfsr_openedmdir_t right_neighbor = { .mdir={.mid=mdir.mid+2, .u.m=mdir.u.m}}; - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // force mdir to compact twice, this should relocate lfsr_mdir_t old_mdir = mdir; @@ -3229,8 +3229,8 @@ code = ''' assert(right_neighbor.mdir.mid == 0*lfsr_mleafweight(&lfs)+2); assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -3296,8 +3296,8 @@ code = ''' &right_neighbor.mdir) => 0; assert(right_neighbor.mdir.u.m.weight == 1); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // cause middle mdir to split lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+1, &mdir) => 0; @@ -3322,8 +3322,8 @@ code = ''' lfsr_mtree_lookup(&lfs, 3*lfsr_mleafweight(&lfs)+0, &mdir) => 0; assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; ''' @@ -3389,8 +3389,8 @@ code = ''' &right_neighbor.mdir) => 0; assert(right_neighbor.mdir.u.m.weight == 1); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_addopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_addopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); // cause middle mdir to drop lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+0, &mdir) => 0; @@ -3410,8 +3410,8 @@ code = ''' lfsr_mtree_lookup(&lfs, 1*lfsr_mleafweight(&lfs)+0, &mdir) => 0; assert(memcmp(&right_neighbor.mdir.u.m, &mdir.u.m, sizeof(mdir.u.m)) == 0); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &left_neighbor); - lfsr_mdir_removeopened(&lfs, LFS_TYPE_REG, &right_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &left_neighbor); + lfsr_mdir_removeopened(&lfs, LFS_TYPE_INTERNAL, &right_neighbor); lfsr_unmount(&lfs) => 0; '''