Эх сурвалжийг харах

Squished in-flight files/dirs into single list

This is an effort to try to consolidate the handling of in-flight files
and dirs opened by the user (and possibly opened internally). Both files
and dirs have metadata state that need to be kept in sync by the commit
logic.

This metadata state is mostly contained in the lfs_mdir_t type, which is
present in both the lfs_file_t and lfs_dir_t. Unfortunately both of
these structs have some relatively unrelated metadata that needs to be
kept in sync:
- Files store an id representing the open file
- Dirs store an id during iteration

While these take up the same space, they unfortunately need to be
managed differently by the commit logic.

The best solution I can come up with is to simple store a general
purpose list and tag both structures with LFS_TYPE_REG and LFS_TYPE_DIR
respectively. This is kinda funky, but wins out over duplicated the
commit logic.
Christopher Haster 7 жил өмнө
parent
commit
35f68d28cc
2 өөрчлөгдсөн 335 нэмэгдсэн , 319 устгасан
  1. 308 301
      lfs.c
  2. 27 18
      lfs.h

+ 308 - 301
lfs.c

@@ -753,8 +753,7 @@ static int lfs_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
 }
 
 // internal dir operations
-static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir,
-        bool split, const lfs_block_t tail[2]) {
+static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
     // allocate pair of dir blocks (backwards, so we write to block 1 first)
     for (int i = 0; i < 2; i++) {
         int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]);
@@ -775,16 +774,205 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir,
     dir->off = sizeof(dir->rev);
     dir->etag = 0;
     dir->count = 0;
-    dir->tail[0] = tail[0];
-    dir->tail[1] = tail[1];
+    dir->tail[0] = 0xffffffff;
+    dir->tail[1] = 0xffffffff;
     dir->erased = false;
-    dir->split = split;
+    dir->split = false;
     lfs_globalzero(&dir->locals);
 
     // don't write out yet, let caller take care of that
     return 0;
 }
 
+static int32_t lfs_dir_find(lfs_t *lfs,
+        lfs_mdir_t *dir, const lfs_block_t pair[2],
+        uint32_t findmask, uint32_t findtag,
+        const void *findbuffer) {
+    dir->pair[0] = pair[0];
+    dir->pair[1] = pair[1];
+    int32_t foundtag = LFS_ERR_NOENT;
+
+    // find the block with the most recent revision
+    uint32_t rev[2];
+    for (int i = 0; i < 2; i++) {
+        int err = lfs_bd_read(lfs, dir->pair[i], 0, &rev[i], sizeof(rev[i]));
+        if (err) {
+            return err;
+        }
+        rev[i] = lfs_fromle32(rev[i]);
+    }
+
+    if (lfs_scmp(rev[1], rev[0]) > 0) {
+        lfs_pairswap(dir->pair);
+        lfs_pairswap(rev);
+    }
+
+    // load blocks and check crc
+    for (int i = 0; i < 2; i++) {
+        lfs_off_t off = sizeof(dir->rev);
+        uint32_t ptag = 0;
+        uint32_t crc = 0xffffffff;
+
+        dir->rev = lfs_tole32(rev[0]);
+        lfs_crc(&crc, &dir->rev, sizeof(dir->rev));
+        dir->rev = lfs_fromle32(dir->rev);
+        dir->off = 0;
+
+        uint32_t tempfoundtag = foundtag;
+        uint16_t tempcount = 0;
+        lfs_block_t temptail[2] = {0xffffffff, 0xffffffff};
+        bool tempsplit = false;
+        lfs_global_t templocals;
+        lfs_globalzero(&templocals);
+
+        while (true) {
+            // extract next tag
+            uint32_t tag;
+            int err = lfs_bd_read(lfs, dir->pair[0],
+                    off, &tag, sizeof(tag));
+            if (err) {
+                return err;
+            }
+
+            lfs_crc(&crc, &tag, sizeof(tag));
+            tag = lfs_fromle32(tag) ^ ptag;
+
+            // next commit not yet programmed
+            if (lfs_tagtype(ptag) == LFS_TYPE_CRC && !lfs_tagisvalid(tag)) {
+                dir->erased = true;
+                break;
+            }
+
+            // check we're in valid range
+            if (off + sizeof(tag)+lfs_tagsize(tag) > lfs->cfg->block_size) {
+                dir->erased = false;
+                break;
+            }
+
+            if (lfs_tagtype(tag) == LFS_TYPE_CRC) {
+                // check the crc attr
+                uint32_t dcrc;
+                int err = lfs_bd_read(lfs, dir->pair[0],
+                        off+sizeof(tag), &dcrc, sizeof(dcrc));
+                if (err) {
+                    return err;
+                }
+                dcrc = lfs_fromle32(dcrc);
+
+                if (crc != dcrc) {
+                    dir->erased = false;
+                    break;
+                }
+
+                foundtag = tempfoundtag;
+                dir->off = off + sizeof(tag)+lfs_tagsize(tag);
+                dir->etag = tag;
+                dir->count = tempcount;
+                dir->tail[0] = temptail[0];
+                dir->tail[1] = temptail[1];
+                dir->split = tempsplit;
+                dir->locals = templocals;
+                crc = 0xffffffff;
+            } else {
+                err = lfs_bd_crc(lfs, dir->pair[0],
+                        off+sizeof(tag), lfs_tagsize(tag), &crc);
+                if (err) {
+                    return err;
+                }
+
+                if (lfs_tagid(tag) < 0x3ff && lfs_tagid(tag) >= tempcount) {
+                    tempcount = lfs_tagid(tag)+1;
+                }
+
+                if (lfs_tagsubtype(tag) == LFS_TYPE_TAIL) {
+                    tempsplit = (lfs_tagtype(tag) & 1);
+                    err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag),
+                            temptail, sizeof(temptail));
+                    if (err) {
+                        return err;
+                    }
+                    lfs_pairfromle32(temptail);
+                } else if (lfs_tagsubtype(tag) == LFS_TYPE_GLOBALS) {
+                    err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag),
+                            &templocals, sizeof(templocals));
+                    if (err) {
+                        return err;
+                    }
+                } else if (lfs_tagsubtype(tag) == LFS_TYPE_DELETE) {
+                    LFS_ASSERT(tempcount > 0);
+                    tempcount -= 1;
+
+                    if (lfs_tagid(tag) == lfs_tagid(tempfoundtag)) {
+                        tempfoundtag = LFS_ERR_NOENT;
+                    } else if (lfs_tagisvalid(tempfoundtag) &&
+                            lfs_tagid(tag) < lfs_tagid(tempfoundtag)) {
+                        tempfoundtag -= LFS_MKTAG(0, 1, 0);
+                    }
+                } else if ((tag & findmask) == (findtag & findmask)) {
+                    int res = lfs_bd_cmp(lfs, dir->pair[0], off+sizeof(tag),
+                            findbuffer, lfs_tagsize(tag));
+                    if (res < 0) {
+                        return res;
+                    }
+
+                    if (res) {
+                        // found a match
+                        tempfoundtag = tag;
+                    }
+                }
+            }
+
+            ptag = tag;
+            off += sizeof(tag)+lfs_tagsize(tag);
+        }
+
+        // consider what we have good enough
+        if (dir->off > 0) {
+            // synthetic move
+            if (lfs_paircmp(dir->pair, lfs_globalmovepair(lfs)) == 0) {
+                if (lfs_globalmoveid(lfs) == lfs_tagid(foundtag)) {
+                    foundtag = LFS_ERR_NOENT;
+                } else if (lfs_tagisvalid(foundtag) &&
+                        lfs_globalmoveid(lfs) < lfs_tagid(foundtag)) {
+                    foundtag -= LFS_MKTAG(0, 1, 0);
+                }
+            }
+
+            return foundtag;
+        }
+
+        // failed, try the other crc?
+        lfs_pairswap(dir->pair);
+        lfs_pairswap(rev);
+    }
+
+    LFS_ERROR("Corrupted dir pair at %d %d", dir->pair[0], dir->pair[1]);
+    return LFS_ERR_CORRUPT;
+}
+
+static int lfs_dir_fetch(lfs_t *lfs,
+        lfs_mdir_t *dir, const lfs_block_t pair[2]) {
+    int32_t res = lfs_dir_find(lfs, dir, pair, 0xffffffff, 0xffffffff, NULL);
+    if (res < 0 && res != LFS_ERR_NOENT) {
+        return res;
+    }
+
+    return 0;
+}
+
+static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir,
+        uint32_t getmask, uint32_t gettag, void *buffer) {
+    int32_t getdiff = 0;
+    if (lfs_paircmp(dir->pair, lfs_globalmovepair(lfs)) == 0 &&
+            lfs_tagid(gettag) <= lfs_globalmoveid(lfs)) {
+        // synthetic moves
+        getdiff = LFS_MKTAG(0, 1, 0);
+    }
+
+    return lfs_commitget(lfs, dir->pair[0], dir->off, dir->etag,
+            getmask, gettag, getdiff, buffer, false);
+}
+
 static int lfs_dir_compact(lfs_t *lfs,
         lfs_mdir_t *dir, const lfs_mattr_t *attrs,
         lfs_mdir_t *source, uint16_t begin, uint16_t end) {
@@ -913,11 +1101,15 @@ split:
         }
 
         lfs_mdir_t tail;
-        int err = lfs_dir_alloc(lfs, &tail, dir->split, dir->tail);
+        int err = lfs_dir_alloc(lfs, &tail);
         if (err) {
             return err;
         }
 
+        tail.split = dir->split;
+        tail.tail[0] = dir->tail[0];
+        tail.tail[1] = dir->tail[1];
+
         err = lfs_dir_compact(lfs, &tail, attrs, dir, ack+1, end);
         if (err) {
             return err;
@@ -966,18 +1158,6 @@ relocate:
         }
     }
 
-    // update any dirs/files that are affected
-    for (int i = 0; i < 2; i++) {
-        for (lfs_file_t *f = ((lfs_file_t**)&lfs->files)[i]; f; f = f->next) {
-            if (lfs_paircmp(f->pair, dir->pair) == 0 &&
-                    f->id >= begin && f->id < end) {
-                f->pair[0] = dir->pair[0];
-                f->pair[1] = dir->pair[1];
-                f->id -= begin;
-            }
-        }
-    }
-
     return 0;
 }
 
@@ -1110,218 +1290,33 @@ compact:
     lfs_globalxor(&lfs->globals, &canceldiff);
 
     // update any directories that are affected
-    for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
+    for (lfs_mlist_t *d = lfs->mlist; d; d = d->next) {
         if (lfs_paircmp(d->m.pair, dir->pair) == 0) {
             d->m = *dir;
-            if (d->id > lfs_tagid(deletetag)) {
-                d->pos -= 1;
-            }
-        }
-    }
-
-    for (int i = 0; i < 2; i++) {
-        for (lfs_file_t *f = ((lfs_file_t**)&lfs->files)[i]; f; f = f->next) {
-            if (f->id == lfs_tagid(deletetag)) {
-                f->pair[0] = 0xffffffff;
-                f->pair[1] = 0xffffffff;
-            } else if (f->id > lfs_tagid(deletetag)) {
-                f->id -= 1;
-            }
-        }
-    }
-
-    return 0;
-}
-
-static int32_t lfs_dir_find(lfs_t *lfs,
-        lfs_mdir_t *dir, const lfs_block_t pair[2],
-        uint32_t findmask, uint32_t findtag,
-        const void *findbuffer) {
-    dir->pair[0] = pair[0];
-    dir->pair[1] = pair[1];
-    int32_t foundtag = LFS_ERR_NOENT;
-
-    // find the block with the most recent revision
-    uint32_t rev[2];
-    for (int i = 0; i < 2; i++) {
-        int err = lfs_bd_read(lfs, dir->pair[i], 0, &rev[i], sizeof(rev[i]));
-        if (err) {
-            return err;
-        }
-        rev[i] = lfs_fromle32(rev[i]);
-    }
-
-    if (lfs_scmp(rev[1], rev[0]) > 0) {
-        lfs_pairswap(dir->pair);
-        lfs_pairswap(rev);
-    }
-
-    // load blocks and check crc
-    for (int i = 0; i < 2; i++) {
-        lfs_off_t off = sizeof(dir->rev);
-        uint32_t ptag = 0;
-        uint32_t crc = 0xffffffff;
-
-        dir->rev = lfs_tole32(rev[0]);
-        lfs_crc(&crc, &dir->rev, sizeof(dir->rev));
-        dir->rev = lfs_fromle32(dir->rev);
-        dir->off = 0;
-
-        uint32_t tempfoundtag = foundtag;
-        uint16_t tempcount = 0;
-        lfs_block_t temptail[2] = {0xffffffff, 0xffffffff};
-        bool tempsplit = false;
-        lfs_global_t templocals;
-        lfs_globalzero(&templocals);
-
-        while (true) {
-            // extract next tag
-            uint32_t tag;
-            int err = lfs_bd_read(lfs, dir->pair[0],
-                    off, &tag, sizeof(tag));
-            if (err) {
-                return err;
-            }
-
-            lfs_crc(&crc, &tag, sizeof(tag));
-            tag = lfs_fromle32(tag) ^ ptag;
-
-            // next commit not yet programmed
-            if (lfs_tagtype(ptag) == LFS_TYPE_CRC && !lfs_tagisvalid(tag)) {
-                dir->erased = true;
-                break;
-            }
-
-            // check we're in valid range
-            if (off + sizeof(tag)+lfs_tagsize(tag) > lfs->cfg->block_size) {
-                dir->erased = false;
-                break;
+            if (d->id == lfs_tagid(deletetag)) {
+                d->m.pair[0] = 0xffffffff;
+                d->m.pair[1] = 0xffffffff;
+            } else if (d->id > lfs_tagid(deletetag)) {
+                d->id -= 1;
+                if (d->type == LFS_TYPE_DIR) {
+                    ((lfs_dir_t*)d)->pos -= 1;
+                }
             }
 
-            if (lfs_tagtype(tag) == LFS_TYPE_CRC) {
-                // check the crc attr
-                uint32_t dcrc;
-                int err = lfs_bd_read(lfs, dir->pair[0],
-                        off+sizeof(tag), &dcrc, sizeof(dcrc));
+            while (d->id >= d->m.count && d->m.split) {
+                // we split and id is on tail now
+                d->id -= d->m.count;
+                int err = lfs_dir_fetch(lfs, &d->m, d->m.tail);
                 if (err) {
                     return err;
                 }
-                dcrc = lfs_fromle32(dcrc);
-
-                if (crc != dcrc) {
-                    dir->erased = false;
-                    break;
-                }
-
-                foundtag = tempfoundtag;
-                dir->off = off + sizeof(tag)+lfs_tagsize(tag);
-                dir->etag = tag;
-                dir->count = tempcount;
-                dir->tail[0] = temptail[0];
-                dir->tail[1] = temptail[1];
-                dir->split = tempsplit;
-                dir->locals = templocals;
-                crc = 0xffffffff;
-            } else {
-                err = lfs_bd_crc(lfs, dir->pair[0],
-                        off+sizeof(tag), lfs_tagsize(tag), &crc);
-                if (err) {
-                    return err;
-                }
-
-                if (lfs_tagid(tag) < 0x3ff && lfs_tagid(tag) >= tempcount) {
-                    tempcount = lfs_tagid(tag)+1;
-                }
-
-                if (lfs_tagsubtype(tag) == LFS_TYPE_TAIL) {
-                    tempsplit = (lfs_tagtype(tag) & 1);
-                    err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag),
-                            temptail, sizeof(temptail));
-                    if (err) {
-                        return err;
-                    }
-                    lfs_pairfromle32(temptail);
-                } else if (lfs_tagsubtype(tag) == LFS_TYPE_GLOBALS) {
-                    err = lfs_bd_read(lfs, dir->pair[0], off+sizeof(tag),
-                            &templocals, sizeof(templocals));
-                    if (err) {
-                        return err;
-                    }
-                } else if (lfs_tagsubtype(tag) == LFS_TYPE_DELETE) {
-                    LFS_ASSERT(tempcount > 0);
-                    tempcount -= 1;
-
-                    if (lfs_tagid(tag) == lfs_tagid(tempfoundtag)) {
-                        tempfoundtag = LFS_ERR_NOENT;
-                    } else if (lfs_tagisvalid(tempfoundtag) &&
-                            lfs_tagid(tag) < lfs_tagid(tempfoundtag)) {
-                        tempfoundtag -= LFS_MKTAG(0, 1, 0);
-                    }
-                } else if ((tag & findmask) == (findtag & findmask)) {
-                    int res = lfs_bd_cmp(lfs, dir->pair[0], off+sizeof(tag),
-                            findbuffer, lfs_tagsize(tag));
-                    if (res < 0) {
-                        return res;
-                    }
-
-                    if (res) {
-                        // found a match
-                        tempfoundtag = tag;
-                    }
-                }
             }
-
-            ptag = tag;
-            off += sizeof(tag)+lfs_tagsize(tag);
         }
-
-        // consider what we have good enough
-        if (dir->off > 0) {
-            // synthetic move
-            if (lfs_paircmp(dir->pair, lfs_globalmovepair(lfs)) == 0) {
-                if (lfs_globalmoveid(lfs) == lfs_tagid(foundtag)) {
-                    foundtag = LFS_ERR_NOENT;
-                } else if (lfs_tagisvalid(foundtag) &&
-                        lfs_globalmoveid(lfs) < lfs_tagid(foundtag)) {
-                    foundtag -= LFS_MKTAG(0, 1, 0);
-                }
-            }
-
-            return foundtag;
-        }
-
-        // failed, try the other crc?
-        lfs_pairswap(dir->pair);
-        lfs_pairswap(rev);
-    }
-
-    LFS_ERROR("Corrupted dir pair at %d %d", dir->pair[0], dir->pair[1]);
-    return LFS_ERR_CORRUPT;
-}
-
-static int lfs_dir_fetch(lfs_t *lfs,
-        lfs_mdir_t *dir, const lfs_block_t pair[2]) {
-    int32_t res = lfs_dir_find(lfs, dir, pair, 0xffffffff, 0xffffffff, NULL);
-    if (res < 0 && res != LFS_ERR_NOENT) {
-        return res;
     }
 
     return 0;
 }
 
-static int32_t lfs_dir_get(lfs_t *lfs, lfs_mdir_t *dir,
-        uint32_t getmask, uint32_t gettag, void *buffer) {
-    int32_t getdiff = 0;
-    if (lfs_paircmp(dir->pair, lfs_globalmovepair(lfs)) == 0 &&
-            lfs_tagid(gettag) <= lfs_globalmoveid(lfs)) {
-        // synthetic moves
-        getdiff = LFS_MKTAG(0, 1, 0);
-    }
-
-    return lfs_commitget(lfs, dir->pair[0], dir->off, dir->etag,
-            getmask, gettag, getdiff, buffer, false);
-}
-
 static int32_t lfs_dir_lookup(lfs_t *lfs, lfs_mdir_t *dir, const char **path) {
     // we reduce path to a single name if we can find it
     const char *name = *path;
@@ -1478,11 +1473,13 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
     lfs_alloc_ack(lfs);
 
     lfs_mdir_t dir;
-    err = lfs_dir_alloc(lfs, &dir, false, cwd.tail);
+    err = lfs_dir_alloc(lfs, &dir);
     if (err) {
         return err;
     }
 
+    dir.tail[0] = cwd.tail[0];
+    dir.tail[1] = cwd.tail[1];
     err = lfs_dir_commit(lfs, &dir, NULL);
     if (err) {
         return err;
@@ -1543,18 +1540,19 @@ int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
     dir->id = 0;
     dir->pos = 0;
 
-    // add to list of directories
-    dir->next = lfs->dirs;
-    lfs->dirs = dir;
+    // add to list of mdirs
+    dir->type = LFS_TYPE_DIR;
+    dir->next = (lfs_dir_t*)lfs->mlist;
+    lfs->mlist = (lfs_mlist_t*)dir;
 
     return 0;
 }
 
 int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
-    // remove from list of directories
-    for (lfs_dir_t **p = &lfs->dirs; *p; p = &(*p)->next) {
-        if (*p == dir) {
-            *p = dir->next;
+    // remove from list of mdirs
+    for (lfs_mlist_t **p = &lfs->mlist; *p; p = &(*p)->next) {
+        if (*p == (lfs_mlist_t*)dir) {
+            *p = (*p)->next;
             break;
         }
     }
@@ -1857,84 +1855,87 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
         }
     }
 
+    // setup simple file details
+    int err = 0;
+    file->cfg = cfg;
+    file->flags = flags;
+    file->pos = 0;
+    file->cache.buffer = NULL;
+
     // allocate entry for file if it doesn't exist
-    lfs_mdir_t cwd;
-    int32_t tag = lfs_dir_lookup(lfs, &cwd, &path);
+    int32_t tag = lfs_dir_lookup(lfs, &file->m, &path);
     if (tag < 0 && !(tag == LFS_ERR_NOENT && path)) {
-        return tag;
+        err = tag;
+        goto cleanup;
     }
 
+    // get id, add to list of mdirs to catch update changes
+    file->id = lfs_tagid(tag);
+    file->type = LFS_TYPE_REG;
+    file->next = (lfs_file_t*)lfs->mlist;
+    lfs->mlist = (lfs_mlist_t*)file;
+
     if (tag == LFS_ERR_NOENT) {
         if (!(flags & LFS_O_CREAT)) {
-            return LFS_ERR_NOENT;
+            err = LFS_ERR_NOENT;
+            goto cleanup;
         }
 
         // check that name fits
         lfs_size_t nlen = strlen(path);
         if (nlen > lfs->name_size) {
-            return LFS_ERR_NAMETOOLONG;
+            err = LFS_ERR_NAMETOOLONG;
+            goto cleanup;
         }
 
         // get next slot and create entry to remember name
-        // TODO do we need to make file registered to list to catch updates from this commit? ie if id/cwd change
-        uint16_t id = cwd.count;
-        int err = lfs_dir_commit(lfs, &cwd,
-                LFS_MKATTR(LFS_TYPE_REG, id, path, nlen,
-                LFS_MKATTR(LFS_TYPE_INLINESTRUCT, id, NULL, 0,
+        file->id = file->m.count;
+        int err = lfs_dir_commit(lfs, &file->m,
+                LFS_MKATTR(LFS_TYPE_REG, file->id, path, nlen,
+                LFS_MKATTR(LFS_TYPE_INLINESTRUCT, file->id, NULL, 0,
                 NULL)));
         if (err) {
-            return err;
-        }
-
-        // TODO should we be catching this here?
-        if (id >= cwd.count) {
-            // catch updates from a compact in the above commit
-            id -= cwd.count;
-            cwd.pair[0] = cwd.tail[0];
-            cwd.pair[1] = cwd.tail[1];
+            err = LFS_ERR_NAMETOOLONG;
+            goto cleanup;
         }
 
-        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, id, 0);
+        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0);
     } else if (flags & LFS_O_EXCL) {
-        return LFS_ERR_EXIST;
+        err = LFS_ERR_EXIST;
+        goto cleanup;
     } else if (lfs_tagtype(tag) != LFS_TYPE_REG) {
-        return LFS_ERR_ISDIR;
+        err = LFS_ERR_ISDIR;
+        goto cleanup;
     } else if (flags & LFS_O_TRUNC) {
         // truncate if requested
-        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, lfs_tagid(tag), 0);
-        flags |= LFS_F_DIRTY;
+        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0);
+        file->flags |= LFS_F_DIRTY;
     } else {
         // try to load what's on disk, if it's inlined we'll fix it later
-        tag = lfs_dir_get(lfs, &cwd, 0x7c3ff000,
-                LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tagid(tag), 8), &file->ctz);
+        tag = lfs_dir_get(lfs, &file->m, 0x7c3ff000,
+                LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz);
         if (tag < 0) {
-            return tag;
+            err = tag;
+            goto cleanup;
         }
         lfs_ctzfromle32(&file->ctz);
     }
 
-    // setup file struct
-    file->cfg = cfg;
-    file->pair[0] = cwd.pair[0];
-    file->pair[1] = cwd.pair[1];
-    file->id = lfs_tagid(tag);
-    file->flags = flags;
-    file->pos = 0;
-
     // fetch attrs
     for (const struct lfs_attr *a = file->cfg->attrs; a; a = a->next) {
         if ((file->flags & 3) != LFS_O_WRONLY) {
-            // TODO what if cwd is invalid from above compact?
-            int32_t res = lfs_dir_get(lfs, &cwd, 0x7ffff000,
+            int32_t res = lfs_dir_get(lfs, &file->m, 0x7ffff000,
                     LFS_MKTAG(0x100 | a->type, file->id, a->size), a->buffer);
             if (res < 0 && res != LFS_ERR_NOENT) {
-                return res;
+                err = res;
+                goto cleanup;
             }
         }
 
         if ((file->flags & 3) != LFS_O_RDONLY) {
             if (a->size > lfs->attr_size) {
-                return LFS_ERR_NOSPC;
+                err = LFS_ERR_NOSPC;
+                goto cleanup;
             }
 
             file->flags |= LFS_F_DIRTY;
@@ -1946,14 +1947,17 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
     if (file->cfg->buffer) {
         file->cache.buffer = file->cfg->buffer;
     } else if ((file->flags & 3) == LFS_O_RDONLY) {
+        // TODO cache_size
         file->cache.buffer = lfs_malloc(lfs->cfg->read_size);
         if (!file->cache.buffer) {
-            return LFS_ERR_NOMEM;
+            err = LFS_ERR_NOMEM;
+            goto cleanup;
         }
     } else {
         file->cache.buffer = lfs_malloc(lfs->cfg->prog_size);
         if (!file->cache.buffer) {
-            return LFS_ERR_NOMEM;
+            err = LFS_ERR_NOMEM;
+            goto cleanup;
         }
     }
 
@@ -1967,21 +1971,23 @@ int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
 
         // don't always read (may be new/trunc file)
         if (file->ctz.size > 0) {
-            int32_t res = lfs_dir_get(lfs, &cwd, 0x7c3ff000,
-                    LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tagid(tag), file->ctz.size),
+            int32_t res = lfs_dir_get(lfs, &file->m, 0x7c3ff000,
+                    LFS_MKTAG(LFS_TYPE_STRUCT, file->id, file->ctz.size),
                     file->cache.buffer);
             if (res < 0) {
-                lfs_free(file->cache.buffer);
-                return res;
+                err = res;
+                goto cleanup;
             }
         }
     }
 
-    // add to list of files
-    file->next = lfs->files;
-    lfs->files = file;
-
     return 0;
+
+cleanup:
+    // clean up lingering resources
+    file->flags |= LFS_F_ERRED;
+    lfs_file_close(lfs, file);
+    return err;
 }
 
 int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
@@ -1993,10 +1999,10 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
 int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
     int err = lfs_file_sync(lfs, file);
 
-    // remove from list of files
-    for (lfs_file_t **p = &lfs->files; *p; p = &(*p)->next) {
-        if (*p == file) {
-            *p = file->next;
+    // remove from list of mdirs
+    for (lfs_mlist_t **p = &lfs->mlist; *p; p = &(*p)->next) {
+        if (*p == (lfs_mlist_t*)file) {
+            *p = (*p)->next;
             break;
         }
     }
@@ -2143,16 +2149,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
 
         if ((file->flags & LFS_F_DIRTY) &&
                 !(file->flags & LFS_F_ERRED) &&
-                !lfs_pairisnull(file->pair)) {
+                !lfs_pairisnull(file->m.pair)) {
             // update dir entry
-            // TODO keep list of dirs including these guys for no
-            // need of another reload?
-            lfs_mdir_t cwd;
-            err = lfs_dir_fetch(lfs, &cwd, file->pair);
-            if (err) {
-                return err;
-            }
-
             uint16_t type;
             const void *buffer;
             lfs_size_t size;
@@ -2170,7 +2168,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
 
             // commit file data and attributes
             lfs_ctztole32(&file->ctz);
-            int err = lfs_dir_commit(lfs, &cwd,
+            int err = lfs_dir_commit(lfs, &file->m,
                     LFS_MKATTR(type, file->id, buffer, size,
                     LFS_MKATTR(LFS_FROM_ATTRS, file->id, file->cfg->attrs, 0,
                     NULL)));
@@ -2815,8 +2813,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
     // setup default state
     lfs->root[0] = 0xffffffff;
     lfs->root[1] = 0xffffffff;
-    lfs->files = NULL;
-    lfs->dirs = NULL;
+    lfs->mlist = NULL;
     lfs_globalones(&lfs->globals);
     lfs_globalzero(&lfs->locals);
 
@@ -2855,16 +2852,14 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
 
     // create superblock dir
     lfs_mdir_t dir;
-    err = lfs_dir_alloc(lfs, &dir, false,
-            (const lfs_block_t[2]){0xffffffff, 0xffffffff});
+    err = lfs_dir_alloc(lfs, &dir);
     if (err) {
         return err;
     }
 
     // write root directory
     lfs_mdir_t root;
-    err = lfs_dir_alloc(lfs, &root, false,
-            (const lfs_block_t[2]){0xffffffff, 0xffffffff});
+    err = lfs_dir_alloc(lfs, &root);
     if (err) {
         return err;
     }
@@ -3054,7 +3049,11 @@ int lfs_fs_traverse(lfs_t *lfs,
     }
 
     // iterate over any open files
-    for (lfs_file_t *f = lfs->files; f; f = f->next) {
+    for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
+        if (f->type != LFS_TYPE_REG) {
+            continue;
+        }
+
         if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) {
             int err = lfs_ctztraverse(lfs, &lfs->rcache, &f->cache,
                     f->ctz.head, f->ctz.size, cb, data);
@@ -3128,6 +3127,21 @@ static int32_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
 
 static int lfs_fs_relocate(lfs_t *lfs,
         const lfs_block_t oldpair[2], lfs_block_t newpair[2]) {
+    // update internal root
+    if (lfs_paircmp(oldpair, lfs->root) == 0) {
+        LFS_DEBUG("Relocating root %d %d", newpair[0], newpair[1]);
+        lfs->root[0] = newpair[0];
+        lfs->root[1] = newpair[1];
+    }
+
+    // update internally tracked dirs
+    for (lfs_mlist_t *d = lfs->mlist; d; d = d->next) {
+        if (lfs_paircmp(oldpair, d->m.pair) == 0) {
+            d->m.pair[0] = newpair[0];
+            d->m.pair[1] = newpair[1];
+        }
+    }
+
     // find parent
     lfs_mdir_t parent;
     int32_t tag = lfs_fs_parent(lfs, oldpair, &parent);
@@ -3145,13 +3159,6 @@ static int lfs_fs_relocate(lfs_t *lfs,
             return err;
         }
 
-        // update internal root
-        if (lfs_paircmp(oldpair, lfs->root) == 0) {
-            LFS_DEBUG("Relocating root %d %d", newpair[0], newpair[1]);
-            lfs->root[0] = newpair[0];
-            lfs->root[1] = newpair[1];
-        }
-
         // clean up bad block, which should now be a desync
         return lfs_fs_forceconsistency(lfs);
     }

+ 27 - 18
lfs.h

@@ -280,6 +280,12 @@ typedef struct lfs_mattr {
     const struct lfs_mattr *next;
 } lfs_mattr_t;
 
+typedef struct lfs_cache {
+    lfs_block_t block;
+    lfs_off_t off;
+    uint8_t *buffer;
+} lfs_cache_t;
+
 typedef union lfs_global {
     uint16_t u16[5];
 } lfs_global_t;
@@ -288,24 +294,37 @@ typedef struct lfs_mdir {
     lfs_block_t pair[2];
     lfs_block_t tail[2];
     uint32_t rev;
-    lfs_off_t off;
     uint32_t etag;
+    lfs_off_t off;
     uint16_t count;
     bool erased;
     bool split;
     lfs_global_t locals;
 } lfs_mdir_t;
 
-typedef struct lfs_cache {
-    lfs_block_t block;
-    lfs_off_t off;
-    uint8_t *buffer;
-} lfs_cache_t;
+typedef struct lfs_mlist {
+    struct lfs_mlist *next;
+    uint16_t id;
+    uint8_t type;
+    lfs_mdir_t m;
+} lfs_mlist_t;
+
+typedef struct lfs_dir {
+    struct lfs_dir *next;
+    uint16_t id;
+    uint8_t type;
+    lfs_mdir_t m;
+
+    lfs_off_t pos;
+    lfs_block_t head[2];
+} lfs_dir_t;
 
 typedef struct lfs_file {
     struct lfs_file *next;
     uint16_t id;
-    lfs_block_t pair[2];
+    uint8_t type;
+    lfs_mdir_t m;
+
     struct lfs_ctz {
         lfs_block_t head;
         lfs_size_t size;
@@ -320,15 +339,6 @@ typedef struct lfs_file {
     const struct lfs_file_config *cfg;
 } lfs_file_t;
 
-typedef struct lfs_dir {
-    struct lfs_dir *next;
-    uint16_t id;
-    struct lfs_mdir m;
-
-    lfs_block_t head[2];
-    lfs_off_t pos;
-} lfs_dir_t;
-
 typedef struct lfs_superblock {
     char magic[8];
     uint32_t version;
@@ -355,8 +365,7 @@ typedef struct lfs {
     lfs_cache_t pcache;
 
     lfs_block_t root[2];
-    lfs_file_t *files;
-    lfs_dir_t *dirs;
+    lfs_mlist_t *mlist;
 
     lfs_global_t globals;
     lfs_global_t locals;