Added support for renaming dirs/files

This commit is contained in:
Christopher Haster 2017-04-15 12:35:20 -05:00
parent 3b1bcbe851
commit bd817abb00
3 changed files with 278 additions and 2 deletions

175
lfs.c
View File

@ -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);

1
lfs.h
View File

@ -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);

View File

@ -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