Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lfs.c
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2017-04-15 20:35:20 +0300
committerChristopher Haster <chaster@utexas.edu>2017-04-18 09:44:01 +0300
commitbd817abb00080898357385b5d358418d3072789d (patch)
tree7e6d98d601d6d4834bb150bb85d4fa7aeadc8a09 /lfs.c
parent3b1bcbe8517db576030450e0a88ff1b274570f58 (diff)
Added support for renaming dirs/files
Diffstat (limited to 'lfs.c')
-rw-r--r--lfs.c175
1 files changed, 174 insertions, 1 deletions
diff --git a/lfs.c b/lfs.c
index 3eda1e7..77c6c9c 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);