From ed674e8414273b9989c131f84087422e65de6feb Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 19 Mar 2017 22:00:56 -0500 Subject: [PATCH] Added support for the basic file operation Missing seek, but these are the core filesystem operations provided by this filesystem: - Read a file - Append to a file Additional work is needed around freeing the previous file, so right now it's limited to appending to existing files, a real append only filesystem. Unfortunately the overhead of the free list with multiple open files is becoming tricky. --- lfs.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++--- lfs.h | 44 ++++++++- lfs_config.h | 1 + 3 files changed, 281 insertions(+), 15 deletions(-) diff --git a/lfs.c b/lfs.c index ab54fa1..67f1d2c 100644 --- a/lfs.c +++ b/lfs.c @@ -67,9 +67,19 @@ static lfs_off_t lfs_inext(lfs_t *lfs, lfs_off_t ioff) { return ioff; } +static lfs_off_t lfs_toindex(lfs_t *lfs, lfs_off_t off) { + lfs_off_t i = 0; + while (off > 512) { + i = lfs_inext(lfs, i); + off -= 512; + } + + return i; +} + // Find index in index chain given its index offset -static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, - lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) { +static lfs_error_t lfs_ifind_block(lfs_t *lfs, lfs_ino_t head, + lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *block) { lfs_size_t wcount = lfs->info.erase_size/4; lfs_off_t iitarget = ioff / wcount; lfs_off_t iicurrent = (icount-1) / wcount; @@ -88,6 +98,18 @@ static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, iicurrent -= 1 << skip; } + *block = head; + return 0; +} + +static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, + lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) { + lfs_size_t wcount = lfs->info.erase_size/4; + int err = lfs_ifind_block(lfs, head, icount, ioff, &head); + if (err) { + return err; + } + return lfs->ops->read(lfs->bd, (void*)ino, head, 4*(ioff % wcount), 4); } @@ -107,7 +129,7 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp, return err; } - lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-1) + 1; + lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-2) + 1; for (lfs_off_t i = 0; i < skips; i++) { err = lfs->ops->write(lfs->bd, (void*)&head, nhead, 4*i, 4); if (err) { @@ -141,20 +163,44 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp, static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) { // TODO save slot for freeing? lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head, - lfs->free.d.icount, lfs->free.d.ioff, ino); + lfs->free.end, lfs->free.off, ino); if (err) { return err; } - lfs->free.d.ioff = lfs_inext(lfs, lfs->free.d.ioff); + lfs->free.off = lfs_inext(lfs, lfs->free.off); return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size); } static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) { - return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.d.icount, ino); + return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.end, ino); } +static lfs_error_t lfs_free_flush(lfs_t *lfs) { + lfs_size_t wcount = lfs->info.erase_size/4; + + for (lfs_word_t i = lfs->free.begin / wcount; + i + wcount < lfs->free.off; i += wcount) { + lfs_ino_t block; + int err = lfs_ifind_block(lfs, lfs->free.d.head, + lfs->free.end, i, &block); + if (err) { + return err; + } + + lfs_free(lfs, block); + } + + if (lfs->free.off != lfs->free.d.off || lfs->free.end != lfs->free.d.end) { + // TODO handle overflow? + lfs->free.d.rev += 1; + } + lfs->free.d.off = lfs->free.off; + lfs->free.d.end = lfs->free.end; + + return 0; +} lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) { uint32_t crc = 0xffffffff; @@ -303,8 +349,7 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) { dir->d.size = sizeof(struct lfs_disk_dir); dir->d.tail[0] = 0; dir->d.tail[1] = 0; - - // TODO sort this out + lfs_free_flush(lfs); dir->d.free = lfs->free.d; if (parent) { @@ -469,6 +514,8 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) { cwd.d.rev += 1; cwd.d.size += entry.d.len; + lfs_free_flush(lfs); + cwd.d.free = lfs->free.d; return lfs_pair_commit(lfs, entry.dir, 3, (struct lfs_commit_region[3]) { @@ -478,6 +525,184 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) { }); } +lfs_error_t 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); + if (err) { + return err; + } + + if (flags & LFS_O_CREAT) { + err = lfs_dir_alloc(lfs, &cwd, path, + &file->entry, sizeof(file->entry.d)+strlen(path)); + if (err && err != LFS_ERROR_EXISTS) { + return err; + } + } else { + err = lfs_dir_find(lfs, &cwd, path, &file->entry); + 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->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; + lfs_free_flush(lfs); + cwd.d.free = lfs->free.d; + + 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 { + file->head = file->entry.d.u.file.head; + file->size = file->entry.d.u.file.size; + file->windex = lfs_toindex(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->info.erase_size) { + file->wblock = file->head; + } else { + int err = lfs_ifind(lfs, file->head, file->windex, + file->windex, &file->wblock); + if (err) { + return err; + } + } + + return 0; + } +} + +lfs_error_t 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); + if (err) { + return err; + } + + file->entry.d.u.file.head = file->head; + file->entry.d.u.file.size = file->size; + + cwd.d.rev += 1; + lfs_free_flush(lfs); + cwd.d.free = lfs->free.d; + + 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}, + }); +} + +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; + lfs_size_t nsize = size; + + while (nsize > 0) { + lfs_off_t woff = file->size % lfs->info.erase_size; + + if (file->size == 0) { + int err = lfs_alloc(lfs, &file->wblock); + if (err) { + return err; + } + + file->head = file->wblock; + file->windex = 0; + } else if (woff == 0) { + // TODO check that 2 blocks are available + // TODO check for available blocks for backing up scratch files? + int err = lfs_alloc(lfs, &file->wblock); + if (err) { + return err; + } + + err = lfs_iappend(lfs, &file->head, &file->windex, file->wblock); + if (err) { + return err; + } + } + + lfs_size_t diff = lfs_min(nsize, lfs->info.erase_size - woff); + int err = lfs->ops->write(lfs->bd, data, file->wblock, woff, diff); + if (err) { + return err; + } + + file->size += diff; + data += diff; + nsize -= diff; + } + + 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; + lfs_size_t nsize = size; + + while (nsize > 0 && file->roff < file->size) { + lfs_off_t roff = file->roff % lfs->info.erase_size; + + // TODO cache index blocks + if (file->size < lfs->info.erase_size) { + file->rblock = file->head; + } else if (roff == 0) { + int err = lfs_ifind(lfs, file->head, file->windex, + file->rindex, &file->rblock); + if (err) { + return err; + } + + file->rindex = lfs_inext(lfs, file->rindex); + } + + lfs_size_t diff = lfs_min( + lfs_min(nsize, file->size-file->roff), + lfs->info.erase_size - roff); + int err = lfs->ops->read(lfs->bd, data, file->rblock, roff, diff); + if (err) { + return err; + } + + file->roff += diff; + data += diff; + nsize -= diff; + } + + return size - nsize; +} + // Little filesystem operations @@ -509,10 +734,14 @@ lfs_error_t lfs_format(lfs_t *lfs) { { // Create free list lfs->free = (lfs_free_t){ - .d.head = 2, - .d.ioff = 1, - .d.icount = 1, + .begin = 1, + .off = 1, + .end = 1, + .d.rev = 1, + .d.head = 2, + .d.off = 1, + .d.end = 1, }; lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size; diff --git a/lfs.h b/lfs.h index c7cb555..00ffbc0 100644 --- a/lfs.h +++ b/lfs.h @@ -26,12 +26,27 @@ enum lfs_type { LFS_TYPE_DIR = 2, }; +enum lfs_open_flags { + LFS_O_RDONLY = 0, + LFS_O_WRONLY = 1, + LFS_O_RDWR = 2, + LFS_O_CREAT = 0x0040, + LFS_O_EXCL = 0x0080, + LFS_O_TRUNC = 0x0200, + LFS_O_APPEND = 0x0400, + LFS_O_SYNC = 0x1000, +}; + typedef struct lfs_free { + lfs_word_t begin; + lfs_word_t off; + lfs_word_t end; + lfs_disk_struct lfs_disk_free { - lfs_ino_t head; - lfs_word_t ioff; - lfs_word_t icount; lfs_word_t rev; + lfs_ino_t head; + lfs_word_t off; + lfs_word_t end; } d; } lfs_free_t; @@ -46,7 +61,7 @@ typedef struct lfs_dir { struct lfs_disk_free free; } d; -} lfs_dir_t; +} lfs_dir_t; typedef struct lfs_entry { lfs_ino_t dir[2]; @@ -65,6 +80,20 @@ typedef struct lfs_entry { } d; } lfs_entry_t; +typedef struct lfs_file { + lfs_ino_t head; + lfs_size_t size; + + lfs_ino_t wblock; + lfs_word_t windex; + + lfs_ino_t rblock; + lfs_word_t rindex; + lfs_off_t roff; + + struct lfs_entry entry; +} lfs_file_t; + typedef struct lfs_superblock { lfs_ino_t pair[2]; lfs_disk_struct lfs_disk_superblock { @@ -94,5 +123,12 @@ lfs_error_t lfs_mount(lfs_t *lfs); lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path); +lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file, + const char *path, int flags); +lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file); +lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, + const void *buffer, lfs_size_t size); +lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, + void *buffer, lfs_size_t size); #endif diff --git a/lfs_config.h b/lfs_config.h index 973b61c..83d9384 100644 --- a/lfs_config.h +++ b/lfs_config.h @@ -15,6 +15,7 @@ typedef uint32_t lfs_word_t; typedef uint16_t lfs_hword_t; typedef lfs_word_t lfs_size_t; +typedef int32_t lfs_ssize_t; typedef lfs_word_t lfs_off_t; typedef int lfs_error_t;