Implemented dir seek/tell (untested) and tweaked recursive removes
Recursive removes is proving more challenging to implement than I expected. The problem with the previous approach is that it moved the mid into a potentially non-sensical position with the expectation it would be updated in lfsr_dir_read because the rid overflows the current weight (since dropping mdirs always set the weight to zero). But if an unrelated mdir commit followed that happened to touch that nonsense mid, the mdir would incorrectly be updated to the previous block, causing problems for the dir's read state. --- The solution here is to toss all of that out and rely solely on directory position updates, which are a bit simpler. So in lfsr_dir_read, if our mid/rid is deleted, we perform a full rewind+seek to the new position. This can be more costly, but since the most common case, recursive removal, leaves us with all mid/rids < pos deleted, it should only add a single mtree lookup per lfsr_dir_read. Also added prototypes for dir seek/tell/rewind, since we're using they're logic for this. Though these aren't yet tested. These are built on the new function lfsr_mtree_seek which captures the common logic of seek over multiple mdirs in the mtree efficiently, and skips unnecessary rid lookups where possible.
This commit is contained in:
@@ -5321,6 +5321,31 @@ static int lfsr_mtree_parent(lfs_t *lfs, lfsr_mptr_t mchild,
|
||||
}
|
||||
}
|
||||
|
||||
static int lfsr_mtree_seek(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
lfs_off_t off) {
|
||||
// calculate new rid
|
||||
lfs_off_t rid_ = *rid + off;
|
||||
// lookup mdirs until we find our rid, we need to do this because
|
||||
// we don't know how many rids are in each mdir until we fetch
|
||||
while (rid_ >= mdir->rbyd.weight) {
|
||||
lfs_ssize_t mid_ = mdir->mid + 1;
|
||||
rid_ -= mdir->rbyd.weight;
|
||||
// end of mtree?
|
||||
if (mid_ >= (lfs_ssize_t)lfsr_mtree_weight(lfs)) {
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
int err = lfsr_mtree_lookup(lfs, mid_, mdir);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
*rid = rid_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// low-level mdir compaction
|
||||
static int lfsr_mdir_compact_(lfs_t *lfs, lfsr_mdir_t *mdir_,
|
||||
lfs_ssize_t mid,
|
||||
@@ -6136,18 +6161,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
|
||||
// first play out any attrs that change our rid
|
||||
for (lfs_size_t i = 0; i < attr_count; i++) {
|
||||
if (opened->mdir.mid == mdir->mid
|
||||
&& opened->rid >= attrs[i].id) {
|
||||
LFS_ASSERT(opened->rid <= (lfs_ssize_t)mdir->rbyd.weight);
|
||||
// removed?
|
||||
if (type == LFS_TYPE_REG
|
||||
&& opened->rid + attrs[i].delta < attrs[i].id) {
|
||||
opened->mdir.mid = LFSR_MID_RM;
|
||||
} else {
|
||||
opened->rid += attrs[i].delta;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust dir positions if any delta changes
|
||||
if (type == LFS_TYPE_DIR
|
||||
&& (opened->mdir.mid > mdir->mid
|
||||
@@ -6155,6 +6168,19 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
&& opened->rid >= attrs[i].id))) {
|
||||
((lfsr_dir_t*)opened)->pos += attrs[i].delta;
|
||||
}
|
||||
|
||||
if (opened->mdir.mid == mdir->mid
|
||||
&& opened->rid >= attrs[i].id) {
|
||||
LFS_ASSERT(opened->rid <= (lfs_ssize_t)mdir->rbyd.weight);
|
||||
// removed?
|
||||
if (opened->rid + attrs[i].delta < attrs[i].id) {
|
||||
// TODO wait we lose any open dir's mid here... so
|
||||
// the pos will no longer get updates...
|
||||
opened->mdir.mid = LFSR_MID_RM;
|
||||
} else {
|
||||
opened->rid += attrs[i].delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update mid if we had a split or drop
|
||||
@@ -6166,11 +6192,6 @@ static int lfsr_mdir_commit(lfs_t *lfs, lfsr_mdir_t *mdir, lfs_ssize_t *rid,
|
||||
opened->mdir = msibling_;
|
||||
} else {
|
||||
opened->mdir = mdir_;
|
||||
// if dropped, rewind to previous mid, this is needed for
|
||||
// dir read to find the next mdir correctly
|
||||
if (type == LFS_TYPE_DIR && mdir_.mid == LFSR_MID_RM) {
|
||||
opened->mdir.mid = mdir->mid - 1;
|
||||
}
|
||||
}
|
||||
} else if (opened->mdir.mid > mdir->mid
|
||||
&& lfsr_btree_weight(&mtree_) != lfsr_mtree_weight(lfs)) {
|
||||
@@ -7473,59 +7494,6 @@ int lfsr_remove(lfs_t *lfs, const char *path) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
||||
// lookup our directory
|
||||
lfsr_mdir_t mdir;
|
||||
lfs_ssize_t rid;
|
||||
lfsr_tag_t tag;
|
||||
int err = lfsr_mtree_pathlookup(lfs, path,
|
||||
&mdir, &rid, &tag,
|
||||
NULL, NULL, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// are we a directory?
|
||||
if (tag != LFSR_TAG_DIR) {
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
// read our did from the mdir, unless we're root
|
||||
lfs_size_t did = 0;
|
||||
if (rid != -1) {
|
||||
lfsr_data_t data;
|
||||
int err = lfsr_mdir_lookup(lfs, &mdir, rid, LFSR_TAG_DID,
|
||||
NULL, &data);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &did);
|
||||
if (d < 0) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
// now lookup our dstart in the mtree
|
||||
err = lfsr_mtree_dnamelookup(lfs, did, NULL, 0,
|
||||
&dir->mdir.mdir, &dir->mdir.rid, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
|
||||
// add to tracked mdirs
|
||||
lfsr_mdir_addopened(lfs, LFS_TYPE_DIR, &dir->mdir);
|
||||
dir->pos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_close(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
// remove from tracked mdirs
|
||||
lfsr_mdir_removeopened(lfs, LFS_TYPE_DIR, &dir->mdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
||||
memset(info, 0, sizeof(struct lfs_info));
|
||||
|
||||
@@ -7554,6 +7522,54 @@ int lfsr_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_open(lfs_t *lfs, lfsr_dir_t *dir, const char *path) {
|
||||
// lookup our directory
|
||||
lfsr_mdir_t mdir;
|
||||
lfs_ssize_t rid;
|
||||
lfsr_tag_t tag;
|
||||
int err = lfsr_mtree_pathlookup(lfs, path,
|
||||
&mdir, &rid, &tag,
|
||||
NULL, NULL, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// are we a directory?
|
||||
if (tag != LFSR_TAG_DIR) {
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
// read our did from the mdir, unless we're root
|
||||
dir->did = 0;
|
||||
if (rid != -1) {
|
||||
lfsr_data_t data;
|
||||
int err = lfsr_mdir_lookup(lfs, &mdir, rid, LFSR_TAG_DID,
|
||||
NULL, &data);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_ssize_t d = lfsr_data_readleb128(lfs, data, 0, &dir->did);
|
||||
if (d < 0) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
// reset pos
|
||||
dir->pos = 0;
|
||||
dir->mdir.mdir.mid = LFSR_MID_RM;
|
||||
|
||||
// add to tracked mdirs
|
||||
lfsr_mdir_addopened(lfs, LFS_TYPE_DIR, &dir->mdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_close(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
// remove from tracked mdirs
|
||||
lfsr_mdir_removeopened(lfs, LFS_TYPE_DIR, &dir->mdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
memset(info, 0, sizeof(struct lfs_info));
|
||||
|
||||
@@ -7570,28 +7586,37 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// lookup the next entry in our dir
|
||||
lfs_ssize_t rid = dir->mdir.rid + 1;
|
||||
// need to lookup the next mdir?
|
||||
if (rid >= (lfs_ssize_t)dir->mdir.mdir.rbyd.weight) {
|
||||
// out of mdirs?
|
||||
lfs_ssize_t mid = dir->mdir.mdir.mid + 1;
|
||||
if (mid >= (lfs_ssize_t)lfsr_mtree_weight(lfs)) {
|
||||
return LFS_ERR_NOENT;
|
||||
}
|
||||
|
||||
int err = lfsr_mtree_lookup(lfs, mid, &dir->mdir.mdir);
|
||||
// If the rid we were at was removed, just rewind and seek to the position
|
||||
// again. This is a bit suboptimal when recursively removing a directory,
|
||||
// but in that case we likely have removed all previous entries so it's not
|
||||
// that bad.
|
||||
lfs_off_t seek = 1;
|
||||
if (dir->mdir.mdir.mid == LFSR_MID_RM) {
|
||||
// lookup our dstart in the mtree
|
||||
int err = lfsr_mtree_dnamelookup(lfs, dir->did, NULL, 0,
|
||||
&dir->mdir.mdir, &dir->mdir.rid, NULL, NULL);
|
||||
if (err) {
|
||||
LFS_ASSERT(err != LFS_ERR_NOENT);
|
||||
return err;
|
||||
}
|
||||
rid = 0;
|
||||
|
||||
// -2 for "." and ".."
|
||||
LFS_ASSERT(dir->pos >= 2);
|
||||
seek += dir->pos-2;
|
||||
}
|
||||
dir->mdir.rid = rid;
|
||||
|
||||
// lookup the next entry in our dir
|
||||
int err = lfsr_mtree_seek(lfs, &dir->mdir.mdir, &dir->mdir.rid, seek);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
dir->pos += 1;
|
||||
|
||||
// lookup our name tag
|
||||
lfsr_tag_t tag;
|
||||
lfsr_data_t data;
|
||||
int err = lfsr_mdir_lookup(lfs, &dir->mdir.mdir, rid, LFSR_TAG_WIDENAME,
|
||||
err = lfsr_mdir_lookup(lfs, &dir->mdir.mdir,
|
||||
dir->mdir.rid, LFSR_TAG_WIDENAME,
|
||||
&tag, &data);
|
||||
if (err) {
|
||||
return err;
|
||||
@@ -7621,8 +7646,31 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfsr_dir_seek(lfs_t *lfs, lfsr_dir_t *dir, lfs_off_t off) {
|
||||
(void)lfs;
|
||||
// set dir pos and mark mdir as dropped, this will cause lfsr_dir_read
|
||||
// to seek to the correct entry on the first call
|
||||
dir->pos = off;
|
||||
dir->mdir.mdir.mid = LFSR_MID_RM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lfs_soff_t lfsr_dir_tell(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
(void)lfs;
|
||||
return dir->pos;
|
||||
}
|
||||
|
||||
int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir) {
|
||||
(void)lfs;
|
||||
// reset pos
|
||||
dir->pos = 0;
|
||||
dir->mdir.mdir.mid = LFSR_MID_RM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Prepare the filesystem for mutation ///
|
||||
|
||||
static int lfsr_fs_fixgrm(lfs_t *lfs) {
|
||||
LFS_ASSERT(lfsr_grm_hasrm(&lfs->grm_));
|
||||
|
||||
|
||||
@@ -420,6 +420,7 @@ typedef struct lfs_dir {
|
||||
|
||||
typedef struct lfsr_dir {
|
||||
lfsr_openedmdir_t mdir;
|
||||
lfs_size_t did;
|
||||
lfs_off_t pos;
|
||||
} lfsr_dir_t;
|
||||
|
||||
@@ -741,6 +742,7 @@ int lfsr_dir_read(lfs_t *lfs, lfsr_dir_t *dir, struct lfs_info *info);
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
||||
int lfsr_dir_seek(lfs_t *lfs, lfsr_dir_t *dir, lfs_off_t off);
|
||||
|
||||
// Return the position of the directory
|
||||
//
|
||||
@@ -749,11 +751,13 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
||||
//
|
||||
// Returns the position of the directory, or a negative error code on failure.
|
||||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
|
||||
lfs_soff_t lfsr_dir_tell(lfs_t *lfs, lfsr_dir_t *dir);
|
||||
|
||||
// Change the position of the directory to the beginning of the directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
||||
int lfsr_dir_rewind(lfs_t *lfs, lfsr_dir_t *dir);
|
||||
|
||||
|
||||
/// Filesystem-level filesystem operations
|
||||
|
||||
Reference in New Issue
Block a user