|
|
@@ -8,7 +8,6 @@
|
|
|
#include "lfs_util.h"
|
|
|
|
|
|
#include <string.h>
|
|
|
-#include <stdbool.h>
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
@@ -1086,6 +1085,43 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
|
|
|
return lfs_dir_commit(lfs, &cwd, &file->entry, NULL);
|
|
|
}
|
|
|
|
|
|
+lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
|
|
+ void *buffer, lfs_size_t size) {
|
|
|
+ uint8_t *data = buffer;
|
|
|
+ size = lfs_min(size, file->entry.d.u.file.size - file->rpos);
|
|
|
+ lfs_size_t nsize = size;
|
|
|
+
|
|
|
+ if ((file->flags & 3) == LFS_O_WRONLY) {
|
|
|
+ return LFS_ERROR_INVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (nsize > 0) {
|
|
|
+ // check if we need a new block
|
|
|
+ if (!file->rblock || file->roff == lfs->cfg->block_size) {
|
|
|
+ int err = lfs_index_find(lfs,
|
|
|
+ file->entry.d.u.file.head, file->entry.d.u.file.size,
|
|
|
+ file->rpos, &file->rblock, &file->roff);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // read as much as we can in current block
|
|
|
+ lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->roff);
|
|
|
+ int err = lfs_bd_read(lfs, file->rblock, file->roff, diff, data);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ file->rpos += diff;
|
|
|
+ file->roff += diff;
|
|
|
+ data += diff;
|
|
|
+ nsize -= diff;
|
|
|
+ }
|
|
|
+
|
|
|
+ return size;
|
|
|
+}
|
|
|
+
|
|
|
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
|
|
const void *buffer, lfs_size_t size) {
|
|
|
const uint8_t *data = buffer;
|
|
|
@@ -1144,43 +1180,6 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
|
|
return size;
|
|
|
}
|
|
|
|
|
|
-lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
|
|
- void *buffer, lfs_size_t size) {
|
|
|
- uint8_t *data = buffer;
|
|
|
- size = lfs_min(size, file->entry.d.u.file.size - file->rpos);
|
|
|
- lfs_size_t nsize = size;
|
|
|
-
|
|
|
- if ((file->flags & 3) == LFS_O_WRONLY) {
|
|
|
- return LFS_ERROR_INVALID;
|
|
|
- }
|
|
|
-
|
|
|
- while (nsize > 0) {
|
|
|
- // check if we need a new block
|
|
|
- if (!file->rblock || file->roff == lfs->cfg->block_size) {
|
|
|
- int err = lfs_index_find(lfs,
|
|
|
- file->entry.d.u.file.head, file->entry.d.u.file.size,
|
|
|
- file->rpos, &file->rblock, &file->roff);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // read as much as we can in current block
|
|
|
- lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->roff);
|
|
|
- int err = lfs_bd_read(lfs, file->rblock, file->roff, diff, data);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- file->rpos += diff;
|
|
|
- file->roff += diff;
|
|
|
- data += diff;
|
|
|
- nsize -= diff;
|
|
|
- }
|
|
|
-
|
|
|
- return size;
|
|
|
-}
|
|
|
-
|
|
|
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
|
|
lfs_soff_t off, int whence) {
|
|
|
// write out everything beforehand, may be noop if rdonly
|
|
|
@@ -1215,10 +1214,6 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
|
|
return prev;
|
|
|
}
|
|
|
|
|
|
-lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
|
|
|
- return lfs_max(file->wpos, file->entry.d.u.file.size);
|
|
|
-}
|
|
|
-
|
|
|
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) {
|
|
|
return file->rpos;
|
|
|
}
|
|
|
@@ -1232,8 +1227,180 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
|
|
|
+ return lfs_max(file->wpos, file->entry.d.u.file.size);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/// General fs oprations ///
|
|
|
+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->root);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ lfs_entry_t entry;
|
|
|
+ err = lfs_dir_find(lfs, &cwd, &entry, &path);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO abstract out info assignment
|
|
|
+ memset(info, 0, sizeof(*info));
|
|
|
+ info->type = entry.d.type & 0xff;
|
|
|
+ if (info->type == LFS_TYPE_REG) {
|
|
|
+ info->size = entry.d.u.file.size;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = lfs_bd_read(lfs, cwd.pair[0], entry.off + sizeof(entry.d),
|
|
|
+ entry.d.len - sizeof(entry.d), info->name);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int lfs_remove(lfs_t *lfs, const char *path) {
|
|
|
+ lfs_dir_t cwd;
|
|
|
+ int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ lfs_entry_t entry;
|
|
|
+ err = lfs_dir_find(lfs, &cwd, &entry, &path);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ lfs_dir_t dir;
|
|
|
+ if (entry.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, entry.d.u.dir);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ } else if (dir.d.size != sizeof(dir.d)) {
|
|
|
+ return LFS_ERROR_INVALID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // remove the entry
|
|
|
+ err = lfs_dir_remove(lfs, &cwd, &entry);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ // if we were a directory, just run a deorphan step, this should
|
|
|
+ // collect us, although is expensive
|
|
|
+ if (entry.d.type == LFS_TYPE_DIR) {
|
|
|
+ int err = lfs_deorphan(lfs);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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->root);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ lfs_entry_t oldentry;
|
|
|
+ err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ // allocate new entry
|
|
|
+ lfs_dir_t newcwd;
|
|
|
+ err = lfs_dir_fetch(lfs, &newcwd, lfs->root);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ lfs_entry_t preventry;
|
|
|
+ err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath);
|
|
|
+ if (err && err != LFS_ERROR_NO_ENTRY) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ bool prevexists = (err != LFS_ERROR_NO_ENTRY);
|
|
|
+
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ if (prevexists) {
|
|
|
+ int err = lfs_dir_commit(lfs, &newcwd, &newentry, newpath);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ int err = lfs_dir_append(lfs, &newcwd, &newentry, 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, &oldentry, &oldpath);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ // remove from old location
|
|
|
+ err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ // if we were a directory, just run a deorphan step, this should
|
|
|
+ // collect us, although is expensive
|
|
|
+ if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
|
|
|
+ int err = lfs_deorphan(lfs);
|
|
|
+ if (err) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
-/// Generic filesystem operations ///
|
|
|
+/// Filesystem operations ///
|
|
|
static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
lfs->cfg = cfg;
|
|
|
lfs->words = lfs->cfg->block_size / sizeof(uint32_t);
|
|
|
@@ -1372,6 +1539,9 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
if (!err) {
|
|
|
err = lfs_bd_read(lfs, dir.pair[0],
|
|
|
sizeof(dir.d), sizeof(superblock.d), &superblock.d);
|
|
|
+
|
|
|
+ lfs->root[0] = superblock.d.root[0];
|
|
|
+ lfs->root[1] = superblock.d.root[1];
|
|
|
}
|
|
|
|
|
|
if (err == LFS_ERROR_CORRUPT ||
|
|
|
@@ -1384,11 +1554,9 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
|
|
|
LFS_ERROR("Invalid version %d.%d\n",
|
|
|
0xffff & (superblock.d.version >> 16),
|
|
|
0xffff & (superblock.d.version >> 0));
|
|
|
+ return LFS_ERROR_INVALID;
|
|
|
}
|
|
|
|
|
|
- lfs->root[0] = superblock.d.root[0];
|
|
|
- lfs->root[1] = superblock.d.root[1];
|
|
|
-
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
@@ -1396,6 +1564,8 @@ int lfs_unmount(lfs_t *lfs) {
|
|
|
return lfs_deinit(lfs);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/// Littlefs specific operations ///
|
|
|
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
|
|
|
// iterate over metadata pairs
|
|
|
lfs_dir_t dir;
|
|
|
@@ -1510,7 +1680,7 @@ int lfs_deorphan(lfs_t *lfs) {
|
|
|
|
|
|
if (!parent) {
|
|
|
// we are an orphan
|
|
|
- LFS_INFO("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
|
|
|
+ LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
|
|
|
|
|
|
pdir.d.tail[0] = cdir.d.tail[0];
|
|
|
pdir.d.tail[1] = cdir.d.tail[1];
|
|
|
@@ -1529,168 +1699,3 @@ int lfs_deorphan(lfs_t *lfs) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int lfs_remove(lfs_t *lfs, const char *path) {
|
|
|
- lfs_dir_t cwd;
|
|
|
- int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- lfs_entry_t entry;
|
|
|
- err = lfs_dir_find(lfs, &cwd, &entry, &path);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- lfs_dir_t dir;
|
|
|
- if (entry.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, entry.d.u.dir);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- } else if (dir.d.size != sizeof(dir.d)) {
|
|
|
- return LFS_ERROR_INVALID;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // remove the entry
|
|
|
- err = lfs_dir_remove(lfs, &cwd, &entry);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- // if we were a directory, just run a deorphan step, this should
|
|
|
- // collect us, although is expensive
|
|
|
- if (entry.d.type == LFS_TYPE_DIR) {
|
|
|
- int err = lfs_deorphan(lfs);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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->root);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- lfs_entry_t oldentry;
|
|
|
- err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- // allocate new entry
|
|
|
- lfs_dir_t newcwd;
|
|
|
- err = lfs_dir_fetch(lfs, &newcwd, lfs->root);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- lfs_entry_t preventry;
|
|
|
- err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath);
|
|
|
- if (err && err != LFS_ERROR_NO_ENTRY) {
|
|
|
- return err;
|
|
|
- }
|
|
|
- bool prevexists = (err != LFS_ERROR_NO_ENTRY);
|
|
|
-
|
|
|
- // 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);
|
|
|
-
|
|
|
- if (prevexists) {
|
|
|
- int err = lfs_dir_commit(lfs, &newcwd, &newentry, newpath);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
- } else {
|
|
|
- int err = lfs_dir_append(lfs, &newcwd, &newentry, 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, &oldentry, &oldpath);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- // remove from old location
|
|
|
- err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- // if we were a directory, just run a deorphan step, this should
|
|
|
- // collect us, although is expensive
|
|
|
- if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
|
|
|
- int err = lfs_deorphan(lfs);
|
|
|
- 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->root);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- lfs_entry_t entry;
|
|
|
- err = lfs_dir_find(lfs, &cwd, &entry, &path);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- // TODO abstract out info assignment
|
|
|
- memset(info, 0, sizeof(*info));
|
|
|
- info->type = entry.d.type & 0xff;
|
|
|
- if (info->type == LFS_TYPE_REG) {
|
|
|
- info->size = entry.d.u.file.size;
|
|
|
- }
|
|
|
-
|
|
|
- err = lfs_bd_read(lfs, cwd.pair[0], entry.off + sizeof(entry.d),
|
|
|
- entry.d.len - sizeof(entry.d), info->name);
|
|
|
- if (err) {
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|