Pārlūkot izejas kodu

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.
Christopher Haster 8 gadi atpakaļ
vecāks
revīzija
ed674e8414
3 mainītis faili ar 281 papildinājumiem un 15 dzēšanām
  1. 240 11
      lfs.c
  2. 40 4
      lfs.h
  3. 1 0
      lfs_config.h

+ 240 - 11
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;

+ 40 - 4
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

+ 1 - 0
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;