diff options
author | Christopher Haster <chaster@utexas.edu> | 2017-04-18 06:27:06 +0300 |
---|---|---|
committer | Christopher Haster <chaster@utexas.edu> | 2017-04-18 09:44:01 +0300 |
commit | 3b9d6630c8fab475307d95824c368d45cd6ba41a (patch) | |
tree | 37171913465bb1f753e8b6469f6f7b695df0795c | |
parent | bd817abb00080898357385b5d358418d3072789d (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-- | Makefile | 2 | ||||
-rw-r--r-- | lfs.c | 866 | ||||
-rw-r--r-- | lfs.h | 19 | ||||
-rw-r--r-- | lfs_util.c | 2 | ||||
-rw-r--r-- | lfs_util.h | 2 | ||||
-rwxr-xr-x | tests/test_format.sh | 6 |
6 files changed, 370 insertions, 527 deletions
@@ -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 ./$< @@ -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; @@ -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; @@ -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, @@ -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 |