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
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2017-04-18 06:27:06 +0300
committerChristopher Haster <chaster@utexas.edu>2017-04-18 09:44:01 +0300
commit3b9d6630c8fab475307d95824c368d45cd6ba41a (patch)
tree37171913465bb1f753e8b6469f6f7b695df0795c
parentbd817abb00080898357385b5d358418d3072789d (diff)
Restructured directory code
After quite a bit of prototyping, settled on the following functions: - lfs_dir_alloc - create a new dir - lfs_dir_fetch - load and check a dir pair from disk - lfs_dir_commit - save a dir pair to disk - lfs_dir_shift - shrink a dir pair to disk - lfs_dir_append - add a dir entry, creating dirs if needed - lfs_dir_remove - remove a dir entry, dropping dirs if needed Additionally, followed through with a few other tweaks
-rw-r--r--Makefile2
-rw-r--r--lfs.c866
-rw-r--r--lfs.h19
-rw-r--r--lfs_util.c2
-rw-r--r--lfs_util.h2
-rwxr-xr-xtests/test_format.sh6
6 files changed, 370 insertions, 527 deletions
diff --git a/Makefile b/Makefile
index 93ba9bb..4303537 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@ size: $(OBJ)
$(SIZE) -t $^
.SUFFIXES:
-test: test_format test_dirs test_files test_alloc test_orphan test_paths
+test: test_format test_dirs test_files test_alloc test_paths test_orphan
test_%: tests/test_%.sh
./$<
diff --git a/lfs.c b/lfs.c
index 77c6c9c..bb01d7d 100644
--- a/lfs.c
+++ b/lfs.c
@@ -56,25 +56,8 @@ static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
return true;
}
-static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
- lfs_off_t off, lfs_size_t size, uint32_t *crc) {
- while (off < size) {
- uint8_t c;
- int err = lfs_bd_read(lfs, block, off, 1, &c);
- if (err) {
- return err;
- }
-
- *crc = lfs_crc(&c, 1, *crc);
- off += 1;
- }
-
- return 0;
-}
-
/// Block allocator ///
-
static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
lfs_t *lfs = p;
@@ -168,6 +151,7 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
}
static int lfs_alloc_erased(lfs_t *lfs, lfs_block_t *block) {
+ // TODO rm me?
int err = lfs_alloc(lfs, block);
if (err) {
return err;
@@ -320,14 +304,17 @@ static int lfs_index_traverse(lfs_t *lfs, lfs_block_t head,
}
-/// Metadata pair operations ///
-
+/// Metadata pair and directory operations ///
static inline void lfs_pairswap(lfs_block_t pair[2]) {
lfs_block_t t = pair[0];
pair[0] = pair[1];
pair[1] = t;
}
+static inline bool lfs_pairisnull(const lfs_block_t pair[2]) {
+ return !pair[0] || !pair[1];
+}
+
static inline int lfs_paircmp(
const lfs_block_t paira[2],
const lfs_block_t pairb[2]) {
@@ -335,97 +322,132 @@ static inline int lfs_paircmp(
(paira[0] == pairb[1] && paira[1] == pairb[0]));
}
-struct lfs_fetch_region {
- lfs_off_t off;
- lfs_size_t size;
- void *data;
-};
+static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
+ // Allocate pair of dir blocks
+ for (int i = 0; i < 2; i++) {
+ int err = lfs_alloc(lfs, &dir->pair[i]);
+ if (err) {
+ return err;
+ }
+ }
+
+ // Rather than clobbering one of the blocks we just pretend
+ // the revision may be valid
+ int err = lfs_bd_read(lfs, dir->pair[0], 0, 4, &dir->d.rev);
+ if (err) {
+ return err;
+ }
-static int lfs_pair_fetch(lfs_t *lfs, lfs_block_t pair[2],
- int count, const struct lfs_fetch_region *regions) {
- int checked = 0;
- int rev = 0;
+ // Set defaults
+ dir->d.rev += 1;
+ dir->d.size = sizeof(dir->d);
+ dir->d.tail[0] = 0;
+ dir->d.tail[1] = 0;
+ dir->off = sizeof(dir->d);
+
+ // Don't write out yet, let caller take care of that
+ return 0;
+}
+
+static int lfs_dir_fetch(lfs_t *lfs,
+ lfs_dir_t *dir, const lfs_block_t pair[2]) {
+ // copy out pair, otherwise may be aliasing dir
+ const lfs_block_t tpair[2] = {pair[0], pair[1]};
+ bool valid = false;
+
+ // check both blocks for the most recent revision
for (int i = 0; i < 2; i++) {
- uint32_t nrev;
- int err = lfs_bd_read(lfs, pair[1], 0, 4, &nrev);
+ struct lfs_disk_dir test;
+ int err = lfs_bd_read(lfs, tpair[i], 0, sizeof(test), &test);
if (err) {
return err;
}
- if (checked > 0 && lfs_scmp(nrev, rev) < 0) {
+ if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) {
continue;
}
uint32_t crc = 0xffffffff;
- err = lfs_bd_crc(lfs, pair[1], 0, lfs->block_size, &crc);
- if (err) {
- return err;
+ crc = lfs_crc(crc, sizeof(test), &test);
+
+ for (lfs_off_t j = sizeof(test); j < lfs->block_size; j += 4) {
+ uint32_t word;
+ int err = lfs_bd_read(lfs, tpair[i], j, 4, &word);
+ if (err) {
+ return err;
+ }
+
+ crc = lfs_crc(crc, 4, &word);
}
if (crc != 0) {
- lfs_pairswap(pair);
+ continue;
}
- checked += 1;
- rev = nrev;
- lfs_pairswap(pair);
- }
+ valid = true;
- if (checked == 0) {
- LFS_ERROR("Corrupted metadata pair at %d %d", pair[0], pair[1]);
- return LFS_ERROR_CORRUPT;
+ // setup dir in case it's valid
+ dir->pair[0] = tpair[(i+0) % 2];
+ dir->pair[1] = tpair[(i+1) % 2];
+ dir->off = sizeof(dir->d);
+ dir->d = test;
}
- for (int i = 0; i < count; i++) {
- int err = lfs_bd_read(lfs, pair[0],
- regions[i].off, regions[i].size, regions[i].data);
- if (err) {
- return err;
- }
+ if (!valid) {
+ LFS_ERROR("Corrupted dir pair at %d %d", tpair[0], tpair[1]);
+ return LFS_ERROR_CORRUPT;
}
return 0;
}
-struct lfs_commit_region {
- lfs_off_t off;
- lfs_size_t size;
- const void *data;
-};
+static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
+ const lfs_entry_t *entry, const void *data) {
+ dir->d.rev += 1;
+ lfs_pairswap(dir->pair);
+
+ int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size);
+ if (err) {
+ return err;
+ }
-static int lfs_pair_commit(lfs_t *lfs, lfs_block_t pair[2],
- int count, const struct lfs_commit_region *regions) {
uint32_t crc = 0xffffffff;
- int err = lfs_bd_erase(lfs, pair[1], 0, lfs->block_size);
+ crc = lfs_crc(crc, sizeof(dir->d), &dir->d);
+ err = lfs_bd_prog(lfs, dir->pair[0], 0, sizeof(dir->d), &dir->d);
if (err) {
return err;
}
- lfs_off_t off = 0;
- while (off < lfs->block_size - 4) {
- if (count > 0 && regions[0].off == off) {
- crc = lfs_crc(regions[0].data, regions[0].size, crc);
- int err = lfs_bd_prog(lfs, pair[1],
- off, regions[0].size, regions[0].data);
+ lfs_off_t off = sizeof(dir->d);
+ lfs_size_t size = 0x7fffffff & dir->d.size;
+ while (off < size) {
+ if (entry && off == entry->off) {
+ crc = lfs_crc(crc, sizeof(entry->d), &entry->d);
+ int err = lfs_bd_prog(lfs, dir->pair[0],
+ off, sizeof(entry->d), &entry->d);
if (err) {
return err;
}
+ off += sizeof(entry->d);
- off += regions[0].size;
- count -= 1;
- regions += 1;
+ if (data) {
+ crc = lfs_crc(crc, entry->d.len - sizeof(entry->d), data);
+ int err = lfs_bd_prog(lfs, dir->pair[0],
+ off, entry->d.len - sizeof(entry->d), data);
+ if (err) {
+ return err;
+ }
+ off += entry->d.len - sizeof(entry->d);
+ }
} else {
- // TODO faster strides?
- // TODO should we start crcing what's already
- // programmed after dir size?
uint8_t data;
- int err = lfs_bd_read(lfs, pair[0], off, 1, &data);
+ int err = lfs_bd_read(lfs, dir->pair[1], off, 1, &data);
if (err) {
return err;
}
- crc = lfs_crc((void*)&data, 1, crc);
- err = lfs_bd_prog(lfs, pair[1], off, 1, &data);
+ crc = lfs_crc(crc, 1, &data);
+ err = lfs_bd_prog(lfs, dir->pair[0], off, 1, &data);
if (err) {
return err;
}
@@ -434,143 +456,163 @@ static int lfs_pair_commit(lfs_t *lfs, lfs_block_t pair[2],
}
}
- err = lfs_bd_prog(lfs, pair[1], lfs->block_size-4, 4, &crc);
- if (err) {
- return err;
+ while (off < lfs->block_size-4) {
+ uint8_t data;
+ int err = lfs_bd_read(lfs, dir->pair[0], off, 1, &data);
+ if (err) {
+ return err;
+ }
+
+ crc = lfs_crc(crc, 1, &data);
+ off += 1;
}
- err = lfs_bd_sync(lfs);
+ err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc);
if (err) {
return err;
}
- lfs_pairswap(pair);
- return 0;
+ return lfs_bd_sync(lfs);
}
-// TODO maybe there is a better abstraction for this?
-static int lfs_pair_shift(lfs_t *lfs, lfs_block_t pair[2],
- int count, const struct lfs_commit_region *regions,
- lfs_off_t blank_start, lfs_size_t blank_size) {
- uint32_t crc = 0xffffffff;
- int err = lfs_bd_erase(lfs, pair[1], 0, lfs->block_size);
+static int lfs_dir_shift(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
+ dir->d.rev += 1;
+ dir->d.size -= entry->d.len;
+ lfs_pairswap(dir->pair);
+
+ int err = lfs_bd_erase(lfs, dir->pair[0], 0, lfs->block_size);
if (err) {
return err;
}
- lfs_off_t woff = 0;
- lfs_off_t roff = 0;
- while (woff < lfs->block_size - 4) {
- if (count > 0 && regions[0].off == woff) {
- crc = lfs_crc(regions[0].data, regions[0].size, crc);
- int err = lfs_bd_prog(lfs, pair[1],
- woff, regions[0].size, regions[0].data);
- if (err) {
- return err;
- }
+ uint32_t crc = 0xffffffff;
+ crc = lfs_crc(crc, sizeof(dir->d), &dir->d);
+ err = lfs_bd_prog(lfs, dir->pair[0], 0, sizeof(dir->d), &dir->d);
+ if (err) {
+ return err;
+ }
- woff += regions[0].size;
- roff += regions[0].size;
- count -= 1;
- regions += 1;
- } else if (roff == blank_start) {
- roff += blank_size;
- } else if (roff < lfs->block_size - 4) {
- // TODO faster strides?
- // TODO should we start crcing what's already
- // programmed after dir size?
+ lfs_off_t woff = sizeof(dir->d);
+ lfs_off_t roff = sizeof(dir->d);
+ lfs_size_t size = 0x7fffffff & dir->d.size;
+ while (woff < size) {
+ if (roff == entry->off) {
+ roff += entry->d.len;
+ } else {
uint8_t data;
- int err = lfs_bd_read(lfs, pair[0], roff, 1, &data);
+ int err = lfs_bd_read(lfs, dir->pair[1], roff, 1, &data);
if (err) {
return err;
}
- crc = lfs_crc((void*)&data, 1, crc);
- err = lfs_bd_prog(lfs, pair[1], woff, 1, &data);
+ crc = lfs_crc(crc, 1, (void*)&data);
+ err = lfs_bd_prog(lfs, dir->pair[0], woff, 1, &data);
if (err) {
return err;
}
woff += 1;
roff += 1;
- } else {
- uint8_t data = 0;
- crc = lfs_crc((void*)&data, 1, crc);
- err = lfs_bd_prog(lfs, pair[1], woff, 1, &data);
- if (err) {
- return err;
- }
-
- woff += 1;
}
}
- err = lfs_bd_prog(lfs, pair[1], lfs->block_size-4, 4, &crc);
- if (err) {
- return err;
+ while (woff < lfs->block_size-4) {
+ uint8_t data;
+ int err = lfs_bd_read(lfs, dir->pair[0], woff, 1, &data);
+ if (err) {
+ return err;
+ }
+
+ crc = lfs_crc(crc, 1, &data);
+ woff += 1;
}
- err = lfs_bd_sync(lfs);
+ err = lfs_bd_prog(lfs, dir->pair[0], lfs->block_size-4, 4, &crc);
if (err) {
return err;
}
- lfs_pairswap(pair);
- return 0;
+ return lfs_bd_sync(lfs);
}
+static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
+ lfs_entry_t *entry, const void *data) {
+ // check if we fit, if top bit is set we do not and move on
+ while (true) {
+ if (dir->d.size + entry->d.len <= lfs->block_size - 4) {
+ entry->pair[0] = dir->pair[0];
+ entry->pair[1] = dir->pair[1];
+ entry->off = dir->d.size;
+ dir->d.size += entry->d.len;
+ return lfs_dir_commit(lfs, dir, entry, data);
+ }
-/// Directory operations ///
+ if (!(0x80000000 & dir->d.size)) {
+ lfs_dir_t newdir;
+ int err = lfs_dir_alloc(lfs, &newdir);
+ if (err) {
+ return err;
+ }
-static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t tail[2]) {
- // Allocate pair of dir blocks
- for (int i = 0; i < 2; i++) {
- int err = lfs_alloc(lfs, &dir->pair[i]);
+ newdir.d.tail[0] = dir->d.tail[0];
+ newdir.d.tail[1] = dir->d.tail[1];
+ entry->pair[0] = newdir.pair[0];
+ entry->pair[1] = newdir.pair[1];
+ entry->off = newdir.d.size;
+ newdir.d.size += entry->d.len;
+ err = lfs_dir_commit(lfs, &newdir, entry, data);
+ if (err) {
+ return err;
+ }
+
+ dir->d.size |= 0x80000000;
+ dir->d.tail[0] = newdir.pair[0];
+ dir->d.tail[1] = newdir.pair[1];
+ return lfs_dir_commit(lfs, dir, NULL, NULL);
+ }
+
+ int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
if (err) {
return err;
}
}
-
- // Rather than clobbering one of the blocks we just pretend
- // the revision may be valid
- int err = lfs_bd_read(lfs, dir->pair[0], 0, 4, &dir->d.rev);
- if (err) {
- return err;
- }
- dir->d.rev += 1;
-
- // Calculate total size
- dir->d.size = sizeof(dir->d);
- dir->off = sizeof(dir->d);
-
- // Other defaults
- dir->d.tail[0] = tail[0];
- dir->d.tail[1] = tail[1];
-
- // Write out to memory
- return lfs_pair_commit(lfs, dir->pair,
- 1, (struct lfs_commit_region[]){
- {0, sizeof(dir->d), &dir->d}
- });
}
-static int lfs_dir_fetch(lfs_t *lfs, lfs_dir_t *dir, lfs_block_t pair[2]) {
- dir->pair[0] = pair[0];
- dir->pair[1] = pair[1];
- dir->off = sizeof(dir->d);
+static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
+ // either shift out the one entry or remove the whole dir block
+ if (dir->d.size == sizeof(dir->d)) {
+ lfs_dir_t pdir;
+ int err = lfs_dir_fetch(lfs, &pdir, lfs->root);
+ if (err) {
+ return err;
+ }
- return lfs_pair_fetch(lfs, dir->pair,
- 1, (struct lfs_fetch_region[1]) {
- {0, sizeof(dir->d), &dir->d}
- });
+ while (lfs_paircmp(pdir.d.tail, dir->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)) {
+ return lfs_dir_shift(lfs, dir, entry);
+ } else {
+ pdir.d.tail[0] = dir->d.tail[0];
+ pdir.d.tail[1] = dir->d.tail[1];
+ return lfs_dir_commit(lfs, &pdir, NULL, NULL);
+ }
+ } else {
+ return lfs_dir_shift(lfs, dir, entry);
+ }
}
static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
while (true) {
if ((0x7fffffff & dir->d.size) - dir->off < sizeof(entry->d)) {
if (!(dir->d.size >> 31)) {
- entry->dir[0] = dir->pair[0];
- entry->dir[1] = dir->pair[1];
+ entry->pair[0] = dir->pair[0];
+ entry->pair[1] = dir->pair[1];
entry->off = dir->off;
return LFS_ERROR_NO_ENTRY;
}
@@ -590,9 +632,10 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
}
dir->off += entry->d.len;
- if (entry->d.type == LFS_TYPE_REG || entry->d.type == LFS_TYPE_DIR) {
- entry->dir[0] = dir->pair[0];
- entry->dir[1] = dir->pair[1];
+ if ((0xff & entry->d.type) == LFS_TYPE_REG ||
+ (0xff & entry->d.type) == LFS_TYPE_DIR) {
+ entry->pair[0] = dir->pair[0];
+ entry->pair[1] = dir->pair[1];
entry->off = dir->off - entry->d.len;
return 0;
}
@@ -600,7 +643,7 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
}
static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
- const char **path, lfs_entry_t *entry) {
+ lfs_entry_t *entry, const char **path) {
const char *pathname = *path;
size_t pathlen;
@@ -652,7 +695,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
continue;
}
- int ret = lfs_bd_cmp(lfs, entry->dir[0],
+ int ret = lfs_bd_cmp(lfs, dir->pair[0],
entry->off + sizeof(entry->d), pathlen, pathname);
if (ret < 0) {
return ret;
@@ -686,58 +729,32 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
return 0;
}
-static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
- const char **path, lfs_entry_t *entry) {
- int err = lfs_dir_find(lfs, dir, path, entry);
- if (err != LFS_ERROR_NO_ENTRY) {
- return err ? err : LFS_ERROR_EXISTS;
- }
-
- // Check if we fit
- if ((0x7fffffff & dir->d.size) + sizeof(entry->d) + strlen(*path)
- > lfs->block_size - 4) {
- lfs_dir_t olddir;
- memcpy(&olddir, dir, sizeof(olddir));
-
- int err = lfs_dir_alloc(lfs, dir, olddir.d.tail);
- if (err) {
- return err;
- }
-
- entry->dir[0] = dir->pair[0];
- entry->dir[1] = dir->pair[1];
- entry->off = dir->off;
-
- olddir.d.rev += 1;
- olddir.d.size |= 1 << 31;
- olddir.d.tail[0] = dir->pair[0];
- olddir.d.tail[1] = dir->pair[1];
- return lfs_pair_commit(lfs, olddir.pair,
- 1, (struct lfs_commit_region[]){
- {0, sizeof(olddir.d), &olddir.d}
- });
- }
-
- return 0;
-}
+/// Top level directory operations ///
int lfs_mkdir(lfs_t *lfs, const char *path) {
- // Allocate entry for directory
+ // fetch parent directory
lfs_dir_t cwd;
- int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
+ int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
if (err) {
return err;
}
lfs_entry_t entry;
- err = lfs_dir_append(lfs, &cwd, &path, &entry);
- if (err) {
- return err;
+ err = lfs_dir_find(lfs, &cwd, &entry, &path);
+ if (err != LFS_ERROR_NO_ENTRY) {
+ return err ? err : LFS_ERROR_EXISTS;
}
// Build up new directory
lfs_dir_t dir;
- err = lfs_dir_alloc(lfs, &dir, cwd.d.tail);
+ err = lfs_dir_alloc(lfs, &dir);
+ if (err) {
+ return err;
+ }
+ dir.d.tail[0] = cwd.d.tail[0];
+ dir.d.tail[1] = cwd.d.tail[1];
+
+ err = lfs_dir_commit(lfs, &dir, NULL, NULL);
if (err) {
return err;
}
@@ -747,27 +764,15 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
entry.d.u.dir[0] = dir.pair[0];
entry.d.u.dir[1] = dir.pair[1];
- cwd.d.rev += 1;
- cwd.d.size += entry.d.len;
cwd.d.tail[0] = dir.pair[0];
cwd.d.tail[1] = dir.pair[1];
- return lfs_pair_commit(lfs, entry.dir,
- 3, (struct lfs_commit_region[3]) {
- {0, sizeof(cwd.d), &cwd.d},
- {entry.off, sizeof(entry.d), &entry.d},
- {entry.off+sizeof(entry.d), entry.d.len - sizeof(entry.d), path}
- });
+ return lfs_dir_append(lfs, &cwd, &entry, path);
}
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
- if (path[0] == '/') {
- dir->pair[0] = lfs->root[0];
- dir->pair[1] = lfs->root[1];
- } else {
- dir->pair[0] = lfs->cwd[0];
- dir->pair[1] = lfs->cwd[1];
- }
+ dir->pair[0] = lfs->root[0];
+ dir->pair[1] = lfs->root[1];
int err = lfs_dir_fetch(lfs, dir, dir->pair);
if (err) {
@@ -779,7 +784,7 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
}
lfs_entry_t entry;
- err = lfs_dir_find(lfs, dir, &path, &entry);
+ err = lfs_dir_find(lfs, dir, &entry, &path);
if (err) {
return err;
} else if (entry.d.type != LFS_TYPE_DIR) {
@@ -827,7 +832,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
info->size = entry.d.u.file.size;
}
- err = lfs_bd_read(lfs, entry.dir[0], entry.off + sizeof(entry.d),
+ err = lfs_bd_read(lfs, dir->pair[0], entry.off + sizeof(entry.d),
entry.d.len - sizeof(entry.d), info->name);
if (err) {
return err;
@@ -837,88 +842,63 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
}
-/// File operations ///
-
+/// Top level file operations ///
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
const char *path, int flags) {
// Allocate entry for file if it doesn't exist
// TODO check open files
lfs_dir_t cwd;
- int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
+ int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
if (err) {
return err;
}
- if (flags & LFS_O_CREAT) {
- err = lfs_dir_append(lfs, &cwd, &path, &file->entry);
- if (err && err != LFS_ERROR_EXISTS) {
- return err;
- }
- } else {
- err = lfs_dir_find(lfs, &cwd, &path, &file->entry);
+ err = lfs_dir_find(lfs, &cwd, &file->entry, &path);
+ if (err && !((flags & LFS_O_CREAT) && err == LFS_ERROR_NO_ENTRY)) {
+ return err;
+ } else if (err != LFS_ERROR_NO_ENTRY &&
+ file->entry.d.type == LFS_TYPE_DIR) {
+ return LFS_ERROR_IS_DIR;
+ }
+
+ if ((flags & LFS_O_CREAT) && err == LFS_ERROR_NO_ENTRY) {
+ // create entry to remember name
+ file->entry.d.type = 1;
+ file->entry.d.len = sizeof(file->entry.d) + strlen(path);
+ file->entry.d.u.file.head = 0;
+ file->entry.d.u.file.size = 0;
+ int err = lfs_dir_append(lfs, &cwd, &file->entry, path);
if (err) {
return err;
}
}
- if ((flags & LFS_O_CREAT) && err != LFS_ERROR_EXISTS) {
- // Store file
- file->head = 0;
- file->size = 0;
- file->wblock = 0;
- file->windex = 0;
- file->rblock = 0;
- file->rindex = 0;
- file->roff = 0;
+ file->head = file->entry.d.u.file.head;
+ file->size = file->entry.d.u.file.size;
+ file->windex = lfs_indexfrom(lfs, file->size);
+ file->rblock = 0;
+ file->rindex = 0;
+ file->roff = 0;
- file->entry.d.type = 1;
- file->entry.d.len = sizeof(file->entry.d) + strlen(path);
- file->entry.d.u.file.head = file->head;
- file->entry.d.u.file.size = file->size;
-
- cwd.d.rev += 1;
- cwd.d.size += file->entry.d.len;
-
- return lfs_pair_commit(lfs, file->entry.dir,
- 3, (struct lfs_commit_region[3]) {
- {0, sizeof(cwd.d), &cwd.d},
- {file->entry.off,
- sizeof(file->entry.d),
- &file->entry.d},
- {file->entry.off+sizeof(file->entry.d),
- file->entry.d.len-sizeof(file->entry.d),
- path}
- });
- } else if (file->entry.d.type == LFS_TYPE_DIR) {
- return LFS_ERROR_IS_DIR;
+ // TODO do this lazily in write?
+ // TODO cow the head i/d block
+ if (file->size < lfs->block_size) {
+ file->wblock = file->head;
} else {
- file->head = file->entry.d.u.file.head;
- file->size = file->entry.d.u.file.size;
- file->windex = lfs_indexfrom(lfs, file->size);
- file->rblock = 0;
- file->rindex = 0;
- file->roff = 0;
-
- // TODO do this lazily in write?
- // TODO cow the head i/d block
- if (file->size < lfs->block_size) {
- file->wblock = file->head;
- } else {
- int err = lfs_index_find(lfs, file->head, file->windex,
- file->windex, &file->wblock);
- if (err) {
- return err;
- }
+ int err = lfs_index_find(lfs, file->head, file->windex,
+ file->windex, &file->wblock);
+ if (err) {
+ return err;
}
-
- return 0;
}
+
+ return 0;
}
int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
// Store file
lfs_dir_t cwd;
- int err = lfs_dir_fetch(lfs, &cwd, file->entry.dir);
+ int err = lfs_dir_fetch(lfs, &cwd, file->entry.pair);
if (err) {
return err;
}
@@ -926,13 +906,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
file->entry.d.u.file.head = file->head;
file->entry.d.u.file.size = file->size;
- cwd.d.rev += 1;
-
- return lfs_pair_commit(lfs, file->entry.dir,
- 3, (struct lfs_commit_region[3]) {
- {0, sizeof(cwd.d), &cwd.d},
- {file->entry.off, sizeof(file->entry.d), &file->entry.d},
- });
+ return lfs_dir_commit(lfs, &cwd, &file->entry, NULL);
}
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
@@ -1090,50 +1064,60 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *config) {
}
// Create free list
- lfs->free.begin = 2;
+ lfs->free.begin = 0;
lfs->free.end = lfs->block_count-1;
+ // Create superblock dir
+ lfs_dir_t superdir;
+ err = lfs_dir_alloc(lfs, &superdir);
+ if (err) {
+ return err;
+ }
+
// Write root directory
lfs_dir_t root;
- err = lfs_dir_alloc(lfs, &root, (lfs_block_t[2]){0, 0});
+ err = lfs_dir_alloc(lfs, &root);
+ if (err) {
+ return err;
+ }
+
+ err = lfs_dir_commit(lfs, &root, NULL, NULL);
if (err) {
return err;
}
+
lfs->root[0] = root.pair[0];
lfs->root[1] = root.pair[1];
- lfs->cwd[0] = root.pair[0];
- lfs->cwd[1] = root.pair[1];
// Write superblocks
lfs_superblock_t superblock = {
- .pair = {0, 1},
- .d.rev = 1,
- .d.size = sizeof(superblock),
- .d.root = {lfs->cwd[0], lfs->cwd[1]},
+ .off = sizeof(superdir.d),
+ .d.type = LFS_TYPE_SUPERBLOCK,
+ .d.len = sizeof(superblock.d),
+ .d.version = 0x00000001,
.d.magic = {"littlefs"},
.d.block_size = lfs->block_size,
.d.block_count = lfs->block_count,
+ .d.root = {lfs->root[0], lfs->root[1]},
};
+ superdir.d.tail[0] = root.pair[0];
+ superdir.d.tail[1] = root.pair[1];
+ superdir.d.size += sizeof(superdir.d);
for (int i = 0; i < 2; i++) {
- int err = lfs_pair_commit(lfs, superblock.pair,
- 1, (struct lfs_commit_region[]){
- {0, sizeof(superblock.d), &superblock.d}
- });
+ // Write both pairs for extra safety, do some finagling to pretend
+ // the superblock is an entry
+ int err = lfs_dir_commit(lfs, &superdir,
+ (const lfs_entry_t*)&superblock,
+ (const struct lfs_disk_entry*)&superblock.d + 1);
if (err) {
- LFS_ERROR("Failed to write superblock at %d", superblock.pair[1]);
+ LFS_ERROR("Failed to write superblock at %d", superdir.pair[0]);
return err;
}
-
- uint32_t crc = 0xffffffff;
- err = lfs_bd_crc(lfs, superblock.pair[0], 0, lfs->block_size, &crc);
- if (err || crc != 0) {
- LFS_ERROR("Failed to write superblock at %d", superblock.pair[0]);
- return err ? err : LFS_ERROR_CORRUPT;
- }
}
- return 0;
+ // sanity check that fetch works
+ return lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
}
int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
@@ -1142,25 +1126,28 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *config) {
return err;
}
- lfs_superblock_t superblock = {
- .pair = {0, 1},
- };
- err = lfs_pair_fetch(lfs, superblock.pair,
- 1, (struct lfs_fetch_region[]){
- {0, sizeof(superblock.d), &superblock.d}
- });
-
- if ((err == LFS_ERROR_CORRUPT ||
- memcmp(superblock.d.magic, "littlefs", 8) != 0)) {
- LFS_ERROR("Invalid superblock at %d %d",
- superblock.pair[0], superblock.pair[1]);
+ lfs_dir_t dir;
+ lfs_superblock_t superblock;
+ err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
+ if (!err) {
+ err = lfs_bd_read(lfs, dir.pair[0],
+ sizeof(dir.d), sizeof(superblock.d), &superblock.d);
+ }
+
+ if (err == LFS_ERROR_CORRUPT ||
+ memcmp(superblock.d.magic, "littlefs", 8) != 0) {
+ LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]);
return LFS_ERROR_CORRUPT;
}
+ if (superblock.d.version > 0x0000ffff) {
+ LFS_ERROR("Invalid version %d.%d\n",
+ 0xffff & (superblock.d.version >> 16),
+ 0xffff & (superblock.d.version >> 0));
+ }
+
lfs->root[0] = superblock.d.root[0];
lfs->root[1] = superblock.d.root[1];
- lfs->cwd[0] = superblock.d.root[0];
- lfs->cwd[1] = superblock.d.root[1];
return err;
}
@@ -1284,16 +1271,12 @@ int lfs_deorphan(lfs_t *lfs) {
if (!parent) {
// we are an orphan
- LFS_INFO("Found orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]);
+ LFS_INFO("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];
- pdir.d.rev += 1;
- err = lfs_pair_commit(lfs, pdir.pair,
- 1, (struct lfs_commit_region[]) {
- {0, sizeof(pdir.d), &pdir.d},
- });
+ err = lfs_dir_commit(lfs, &pdir, NULL, NULL);
if (err) {
return err;
}
@@ -1309,13 +1292,13 @@ int lfs_deorphan(lfs_t *lfs) {
int lfs_remove(lfs_t *lfs, const char *path) {
lfs_dir_t cwd;
- int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
+ int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
if (err) {
return err;
}
lfs_entry_t entry;
- err = lfs_dir_find(lfs, &cwd, &path, &entry);
+ err = lfs_dir_find(lfs, &cwd, &entry, &path);
if (err) {
return err;
}
@@ -1333,85 +1316,18 @@ int lfs_remove(lfs_t *lfs, const char *path) {
}
}
- cwd.d.rev += 1;
- cwd.d.size -= entry.d.len;
-
- // either shift out the one entry or remove the whole dir block
- if (cwd.d.size == sizeof(cwd.d)) {
- lfs_dir_t pdir;
- int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd);
- if (err) {
- return err;
- }
-
- while (lfs_paircmp(pdir.d.tail, cwd.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, entry.dir,
- 1, (struct lfs_commit_region[]) {
- {0, sizeof(cwd.d), &cwd.d},
- },
- entry.off, entry.d.len);
- if (err) {
- return err;
- }
- } else {
- pdir.d.tail[0] = cwd.d.tail[0];
- pdir.d.tail[1] = cwd.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, entry.dir,
- 1, (struct lfs_commit_region[]) {
- {0, sizeof(cwd.d), &cwd.d},
- },
- entry.off, entry.d.len);
- if (err) {
- return err;
- }
+ // 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) {
- // remove ourselves from the dir list
- // this may create an orphan, which must be deorphaned
- lfs_dir_t pdir;
- memcpy(&pdir, &cwd, sizeof(pdir));
-
- while (pdir.d.tail[0]) {
- if (lfs_paircmp(pdir.d.tail, entry.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;
- }
+ int err = lfs_deorphan(lfs);
+ if (err) {
+ return err;
}
}
@@ -1419,32 +1335,32 @@ int lfs_remove(lfs_t *lfs, const char *path) {
}
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
- // Find old entry
+ // find old entry
lfs_dir_t oldcwd;
- int err = lfs_dir_fetch(lfs, &oldcwd, lfs->cwd);
+ int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root);
if (err) {
return err;
}
lfs_entry_t oldentry;
- err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry);
+ err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
if (err) {
return err;
}
- // Allocate new entry
+ // allocate new entry
lfs_dir_t newcwd;
- err = lfs_dir_fetch(lfs, &newcwd, lfs->cwd);
+ err = lfs_dir_fetch(lfs, &newcwd, lfs->root);
if (err) {
return err;
}
lfs_entry_t preventry;
- err = lfs_dir_append(lfs, &newcwd, &newpath, &preventry);
- if (err && err != LFS_ERROR_EXISTS) {
+ err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath);
+ if (err && err != LFS_ERROR_NO_ENTRY) {
return err;
}
- bool prevexists = (err == LFS_ERROR_EXISTS);
+ bool prevexists = (err != LFS_ERROR_NO_ENTRY);
// must have same type
if (prevexists && preventry.d.type != oldentry.d.type) {
@@ -1464,28 +1380,21 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
}
}
- // Move to new location
+ // 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;
+ 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
@@ -1495,97 +1404,24 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
return err;
}
- err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry);
+ err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
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;
- }
+ // remove from old location
+ err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
+ if (err) {
+ return err;
}
- // TODO abstract this out for rename + remove?
+ // 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) {
- // 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);
+ int err = lfs_deorphan(lfs);
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;
@@ -1593,13 +1429,13 @@ 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) {
lfs_dir_t cwd;
- int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);
+ int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
if (err) {
return err;
}
lfs_entry_t entry;
- err = lfs_dir_find(lfs, &cwd, &path, &entry);
+ err = lfs_dir_find(lfs, &cwd, &entry, &path);
if (err) {
return err;
}
@@ -1611,7 +1447,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
info->size = entry.d.u.file.size;
}
- err = lfs_bd_read(lfs, entry.dir[0], entry.off + sizeof(entry.d),
+ 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;
diff --git a/lfs.h b/lfs.h
index 3ecf51c..743e2a9 100644
--- a/lfs.h
+++ b/lfs.h
@@ -24,8 +24,9 @@ enum lfs_error {
};
enum lfs_type {
- LFS_TYPE_REG = 1,
- LFS_TYPE_DIR = 2,
+ LFS_TYPE_REG = 0x01,
+ LFS_TYPE_DIR = 0x02,
+ LFS_TYPE_SUPERBLOCK = 0x10,
};
enum lfs_open_flags {
@@ -58,7 +59,7 @@ struct lfs_info {
};
typedef struct lfs_entry {
- lfs_block_t dir[2];
+ lfs_block_t pair[2];
lfs_off_t off;
struct lfs_disk_entry {
@@ -100,14 +101,17 @@ typedef struct lfs_dir {
} lfs_dir_t;
typedef struct lfs_superblock {
- lfs_block_t pair[2];
+ lfs_block_t dir[2]; //TODO rm me?
+ lfs_off_t off;
+
struct lfs_disk_superblock {
- uint32_t rev;
- uint32_t size;
- lfs_block_t root[2];
+ uint16_t type;
+ uint16_t len;
+ uint32_t version;
char magic[8];
uint32_t block_size;
uint32_t block_count;
+ lfs_block_t root[2];
} d;
} lfs_superblock_t;
@@ -123,7 +127,6 @@ typedef struct lfs {
const struct lfs_bd_ops *bd_ops;
lfs_block_t root[2];
- lfs_block_t cwd[2];
struct {
lfs_block_t begin;
lfs_block_t end;
diff --git a/lfs_util.c b/lfs_util.c
index 9b0f66b..edac0a5 100644
--- a/lfs_util.c
+++ b/lfs_util.c
@@ -7,7 +7,7 @@
#include "lfs_util.h"
-uint32_t lfs_crc(const void *buffer, lfs_size_t size, uint32_t crc) {
+uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
diff --git a/lfs_util.h b/lfs_util.h
index 6211328..83273a4 100644
--- a/lfs_util.h
+++ b/lfs_util.h
@@ -31,7 +31,7 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}
-uint32_t lfs_crc(const void *buffer, lfs_size_t size, uint32_t crc);
+uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer);
diff --git a/tests/test_format.sh b/tests/test_format.sh
index 1e12885..0b53dc5 100755
--- a/tests/test_format.sh
+++ b/tests/test_format.sh
@@ -11,13 +11,17 @@ TEST
echo "--- Invalid superblocks ---"
ln -f -s /dev/null blocks/0
+ln -f -s /dev/null blocks/1
tests/test.py << TEST
lfs_format(&lfs, &config) => LFS_ERROR_CORRUPT;
TEST
-rm blocks/0
+rm blocks/0 blocks/1
echo "--- Basic mounting ---"
tests/test.py << TEST
+ lfs_format(&lfs, &config) => 0;
+TEST
+tests/test.py << TEST
lfs_mount(&lfs, &config) => 0;
lfs_unmount(&lfs) => 0;
TEST