From bd817abb00080898357385b5d358418d3072789d Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sat, 15 Apr 2017 12:35:20 -0500 Subject: [PATCH] Added support for renaming dirs/files --- lfs.c | 175 ++++++++++++++++++++++++++++++++++++++++++++- lfs.h | 1 + tests/test_dirs.sh | 104 ++++++++++++++++++++++++++- 3 files changed, 278 insertions(+), 2 deletions(-) diff --git a/lfs.c b/lfs.c index 3eda1e744ae..77c6c9ca06d 100644 --- a/lfs.c +++ b/lfs.c @@ -1337,7 +1337,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { cwd.d.size -= entry.d.len; // either shift out the one entry or remove the whole dir block - if (cwd.d.size == sizeof(dir.d)) { + if (cwd.d.size == sizeof(cwd.d)) { lfs_dir_t pdir; int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd); if (err) { @@ -1418,6 +1418,179 @@ int lfs_remove(lfs_t *lfs, const char *path) { return 0; } +int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { + // Find old entry + lfs_dir_t oldcwd; + int err = lfs_dir_fetch(lfs, &oldcwd, lfs->cwd); + if (err) { + return err; + } + + lfs_entry_t oldentry; + err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry); + if (err) { + return err; + } + + // Allocate new entry + lfs_dir_t newcwd; + err = lfs_dir_fetch(lfs, &newcwd, lfs->cwd); + if (err) { + return err; + } + + lfs_entry_t preventry; + err = lfs_dir_append(lfs, &newcwd, &newpath, &preventry); + if (err && err != LFS_ERROR_EXISTS) { + return err; + } + bool prevexists = (err == LFS_ERROR_EXISTS); + + // must have same type + if (prevexists && preventry.d.type != oldentry.d.type) { + return LFS_ERROR_INVALID; + } + + lfs_dir_t dir; + if (prevexists && preventry.d.type == LFS_TYPE_DIR) { + // must be empty before removal, checking size + // without masking top bit checks for any case where + // dir is not empty + int err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir); + if (err) { + return err; + } else if (dir.d.size != sizeof(dir.d)) { + return LFS_ERROR_INVALID; + } + } + + // Move to new location + lfs_entry_t newentry = preventry; + newentry.d = oldentry.d; + newentry.d.len = sizeof(newentry.d) + strlen(newpath); + + newcwd.d.rev += 1; + if (!prevexists) { + newcwd.d.size += newentry.d.len; + } + + err = lfs_pair_commit(lfs, newentry.dir, + 3, (struct lfs_commit_region[3]) { + {0, sizeof(newcwd.d), &newcwd.d}, + {newentry.off, + sizeof(newentry.d), + &newentry.d}, + {newentry.off+sizeof(newentry.d), + newentry.d.len - sizeof(newentry.d), + newpath} + }); + if (err) { + return err; + } + + // fetch again in case newcwd == oldcwd + // TODO handle this better? + err = lfs_dir_fetch(lfs, &oldcwd, oldcwd.pair); + if (err) { + return err; + } + + err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry); + if (err) { + return err; + } + + // Remove from old location + // TODO abstract this out for rename + remove? + oldcwd.d.rev += 1; + oldcwd.d.size -= oldentry.d.len; + + // either shift out the one entry or remove the whole dir block + if (oldcwd.d.size == sizeof(oldcwd.d)) { + lfs_dir_t pdir; + int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd); + if (err) { + return err; + } + + while (lfs_paircmp(pdir.d.tail, oldcwd.pair) != 0) { + int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail); + if (err) { + return err; + } + } + + // TODO easier check for head block? (common case) + if (!(pdir.d.size & 0x80000000)) { + int err = lfs_pair_shift(lfs, oldentry.dir, + 1, (struct lfs_commit_region[]) { + {0, sizeof(oldcwd.d), &oldcwd.d}, + }, + oldentry.off, oldentry.d.len); + if (err) { + return err; + } + } else { + pdir.d.tail[0] = oldcwd.d.tail[0]; + pdir.d.tail[1] = oldcwd.d.tail[1]; + pdir.d.rev += 1; + + err = lfs_pair_commit(lfs, pdir.pair, + 1, (struct lfs_commit_region[]) { + {0, sizeof(pdir.d), &pdir.d}, + }); + if (err) { + return err; + } + } + } else { + int err = lfs_pair_shift(lfs, oldentry.dir, + 1, (struct lfs_commit_region[]) { + {0, sizeof(oldcwd.d), &oldcwd.d}, + }, + oldentry.off, oldentry.d.len); + if (err) { + return err; + } + } + + // TODO abstract this out for rename + remove? + if (prevexists && preventry.d.type == LFS_TYPE_DIR) { + // remove dest from the dir list + // this may create an orphan, which must be deorphaned + lfs_dir_t pdir; + int err = lfs_dir_fetch(lfs, &pdir, lfs->root); + if (err) { + return err; + } + + while (pdir.d.tail[0]) { + if (lfs_paircmp(pdir.d.tail, preventry.d.u.dir) == 0) { + pdir.d.tail[0] = dir.d.tail[0]; + pdir.d.tail[1] = dir.d.tail[1]; + pdir.d.rev += 1; + + int err = lfs_pair_commit(lfs, pdir.pair, + 1, (struct lfs_commit_region[]) { + {0, sizeof(pdir.d), &pdir.d}, + }); + if (err) { + return err; + } + + break; + } + + int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail); + if (err) { + return err; + } + } + } + + return 0; +} + int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { lfs_dir_t cwd; int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd); diff --git a/lfs.h b/lfs.h index 43de3ee45d8..3ecf51c2359 100644 --- a/lfs.h +++ b/lfs.h @@ -138,6 +138,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *config); int lfs_unmount(lfs_t *lfs); int lfs_remove(lfs_t *lfs, const char *path); +int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); int lfs_mkdir(lfs_t *lfs, const char *path); diff --git a/tests/test_dirs.sh b/tests/test_dirs.sh index 4026c3f9d8c..6d73e7a95bd 100755 --- a/tests/test_dirs.sh +++ b/tests/test_dirs.sh @@ -123,7 +123,7 @@ tests/test.py << TEST lfs_unmount(&lfs) => 0; TEST -echo "--- Directory deletion ---" +echo "--- Directory remove ---" tests/test.py << TEST lfs_mount(&lfs, &config) => 0; lfs_remove(&lfs, "potato") => LFS_ERROR_INVALID; @@ -180,5 +180,107 @@ tests/test.py << TEST lfs_unmount(&lfs) => 0; TEST +echo "--- Directory rename ---" +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_mkdir(&lfs, "coldpotato") => 0; + lfs_mkdir(&lfs, "coldpotato/baked") => 0; + lfs_mkdir(&lfs, "coldpotato/sweet") => 0; + lfs_mkdir(&lfs, "coldpotato/fried") => 0; + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_rename(&lfs, "coldpotato", "hotpotato") => 0; + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, ".") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "..") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "baked") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "sweet") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "fried") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 0; + lfs_dir_close(&lfs, &dir[0]) => 0; + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_mkdir(&lfs, "warmpotato") => 0; + lfs_mkdir(&lfs, "warmpotato/mushy") => 0; + lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERROR_INVALID; + + lfs_remove(&lfs, "warmpotato/mushy") => 0; + lfs_rename(&lfs, "hotpotato", "warmpotato") => 0; + + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, ".") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "..") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "baked") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "sweet") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "fried") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 0; + lfs_dir_close(&lfs, &dir[0]) => 0; + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_mkdir(&lfs, "coldpotato") => 0; + lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0; + lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0; + lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0; + lfs_remove(&lfs, "coldpotato") => LFS_ERROR_INVALID; + lfs_remove(&lfs, "warmpotato") => 0; + lfs_unmount(&lfs) => 0; +TEST +tests/test.py << TEST + lfs_mount(&lfs, &config) => 0; + lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, ".") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "..") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "baked") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "sweet") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 1; + strcmp(info.name, "fried") => 0; + info.type => LFS_TYPE_DIR; + lfs_dir_read(&lfs, &dir[0], &info) => 0; + lfs_dir_close(&lfs, &dir[0]) => 0; + lfs_unmount(&lfs) => 0; +TEST + echo "--- Results ---" tests/stats.py