Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lfs.c Source File

lfs.c

00001 /*
00002  * The little filesystem
00003  *
00004  * Copyright (c) 2017 ARM Limited
00005  *
00006  * Licensed under the Apache License, Version 2.0 (the "License");
00007  * you may not use this file except in compliance with the License.
00008  * You may obtain a copy of the License at
00009  *
00010  *     http://www.apache.org/licenses/LICENSE-2.0
00011  *
00012  * Unless required by applicable law or agreed to in writing, software
00013  * distributed under the License is distributed on an "AS IS" BASIS,
00014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  * See the License for the specific language governing permissions and
00016  * limitations under the License.
00017  */
00018 #include "lfs.h"
00019 #include "lfs_util.h"
00020 
00021 
00022 /// Caching block device operations ///
00023 static int lfs_cache_read(lfs_t *lfs, lfs_cache_t *rcache,
00024         const lfs_cache_t *pcache, lfs_block_t block,
00025         lfs_off_t off, void *buffer, lfs_size_t size) {
00026     uint8_t *data = buffer;
00027     LFS_ASSERT(block < lfs->cfg->block_count);
00028 
00029     while (size > 0) {
00030         if (pcache && block == pcache->block && off >= pcache->off &&
00031                 off < pcache->off + lfs->cfg->prog_size) {
00032             // is already in pcache?
00033             lfs_size_t diff = lfs_min(size,
00034                     lfs->cfg->prog_size - (off-pcache->off));
00035             memcpy(data, &pcache->buffer[off-pcache->off], diff);
00036 
00037             data += diff;
00038             off += diff;
00039             size -= diff;
00040             continue;
00041         }
00042 
00043         if (block == rcache->block && off >= rcache->off &&
00044                 off < rcache->off + lfs->cfg->read_size) {
00045             // is already in rcache?
00046             lfs_size_t diff = lfs_min(size,
00047                     lfs->cfg->read_size - (off-rcache->off));
00048             memcpy(data, &rcache->buffer[off-rcache->off], diff);
00049 
00050             data += diff;
00051             off += diff;
00052             size -= diff;
00053             continue;
00054         }
00055 
00056         if (off % lfs->cfg->read_size == 0 && size >= lfs->cfg->read_size) {
00057             // bypass cache?
00058             lfs_size_t diff = size - (size % lfs->cfg->read_size);
00059             int err = lfs->cfg->read(lfs->cfg, block, off, data, diff);
00060             if (err) {
00061                 return err;
00062             }
00063 
00064             data += diff;
00065             off += diff;
00066             size -= diff;
00067             continue;
00068         }
00069 
00070         // load to cache, first condition can no longer fail
00071         rcache->block = block;
00072         rcache->off = off - (off % lfs->cfg->read_size);
00073         int err = lfs->cfg->read(lfs->cfg, rcache->block,
00074                 rcache->off, rcache->buffer, lfs->cfg->read_size);
00075         if (err) {
00076             return err;
00077         }
00078     }
00079 
00080     return 0;
00081 }
00082 
00083 static int lfs_cache_cmp(lfs_t *lfs, lfs_cache_t *rcache,
00084         const lfs_cache_t *pcache, lfs_block_t block,
00085         lfs_off_t off, const void *buffer, lfs_size_t size) {
00086     const uint8_t *data = buffer;
00087 
00088     for (lfs_off_t i = 0; i < size; i++) {
00089         uint8_t c;
00090         int err = lfs_cache_read(lfs, rcache, pcache,
00091                 block, off+i, &c, 1);
00092         if (err) {
00093             return err;
00094         }
00095 
00096         if (c != data[i]) {
00097             return false;
00098         }
00099     }
00100 
00101     return true;
00102 }
00103 
00104 static int lfs_cache_crc(lfs_t *lfs, lfs_cache_t *rcache,
00105         const lfs_cache_t *pcache, lfs_block_t block,
00106         lfs_off_t off, lfs_size_t size, uint32_t *crc) {
00107     for (lfs_off_t i = 0; i < size; i++) {
00108         uint8_t c;
00109         int err = lfs_cache_read(lfs, rcache, pcache,
00110                 block, off+i, &c, 1);
00111         if (err) {
00112             return err;
00113         }
00114 
00115         lfs_crc(crc, &c, 1);
00116     }
00117 
00118     return 0;
00119 }
00120 
00121 static int lfs_cache_flush(lfs_t *lfs,
00122         lfs_cache_t *pcache, lfs_cache_t *rcache) {
00123     if (pcache->block != 0xffffffff) {
00124         int err = lfs->cfg->prog(lfs->cfg, pcache->block,
00125                 pcache->off, pcache->buffer, lfs->cfg->prog_size);
00126         if (err) {
00127             return err;
00128         }
00129 
00130         if (rcache) {
00131             int res = lfs_cache_cmp(lfs, rcache, NULL, pcache->block,
00132                     pcache->off, pcache->buffer, lfs->cfg->prog_size);
00133             if (res < 0) {
00134                 return res;
00135             }
00136 
00137             if (!res) {
00138                 return LFS_ERR_CORRUPT;
00139             }
00140         }
00141 
00142         pcache->block = 0xffffffff;
00143     }
00144 
00145     return 0;
00146 }
00147 
00148 static int lfs_cache_prog(lfs_t *lfs, lfs_cache_t *pcache,
00149         lfs_cache_t *rcache, lfs_block_t block,
00150         lfs_off_t off, const void *buffer, lfs_size_t size) {
00151     const uint8_t *data = buffer;
00152     LFS_ASSERT(block < lfs->cfg->block_count);
00153 
00154     while (size > 0) {
00155         if (block == pcache->block && off >= pcache->off &&
00156                 off < pcache->off + lfs->cfg->prog_size) {
00157             // is already in pcache?
00158             lfs_size_t diff = lfs_min(size,
00159                     lfs->cfg->prog_size - (off-pcache->off));
00160             memcpy(&pcache->buffer[off-pcache->off], data, diff);
00161 
00162             data += diff;
00163             off += diff;
00164             size -= diff;
00165 
00166             if (off % lfs->cfg->prog_size == 0) {
00167                 // eagerly flush out pcache if we fill up
00168                 int err = lfs_cache_flush(lfs, pcache, rcache);
00169                 if (err) {
00170                     return err;
00171                 }
00172             }
00173 
00174             continue;
00175         }
00176 
00177         // pcache must have been flushed, either by programming and
00178         // entire block or manually flushing the pcache
00179         LFS_ASSERT(pcache->block == 0xffffffff);
00180 
00181         if (off % lfs->cfg->prog_size == 0 &&
00182                 size >= lfs->cfg->prog_size) {
00183             // bypass pcache?
00184             lfs_size_t diff = size - (size % lfs->cfg->prog_size);
00185             int err = lfs->cfg->prog(lfs->cfg, block, off, data, diff);
00186             if (err) {
00187                 return err;
00188             }
00189 
00190             if (rcache) {
00191                 int res = lfs_cache_cmp(lfs, rcache, NULL,
00192                         block, off, data, diff);
00193                 if (res < 0) {
00194                     return res;
00195                 }
00196 
00197                 if (!res) {
00198                     return LFS_ERR_CORRUPT;
00199                 }
00200             }
00201 
00202             data += diff;
00203             off += diff;
00204             size -= diff;
00205             continue;
00206         }
00207 
00208         // prepare pcache, first condition can no longer fail
00209         pcache->block = block;
00210         pcache->off = off - (off % lfs->cfg->prog_size);
00211     }
00212 
00213     return 0;
00214 }
00215 
00216 
00217 /// General lfs block device operations ///
00218 static int lfs_bd_read(lfs_t *lfs, lfs_block_t block,
00219         lfs_off_t off, void *buffer, lfs_size_t size) {
00220     // if we ever do more than writes to alternating pairs,
00221     // this may need to consider pcache
00222     return lfs_cache_read(lfs, &lfs->rcache, NULL,
00223             block, off, buffer, size);
00224 }
00225 
00226 static int lfs_bd_prog(lfs_t *lfs, lfs_block_t block,
00227         lfs_off_t off, const void *buffer, lfs_size_t size) {
00228     return lfs_cache_prog(lfs, &lfs->pcache, NULL,
00229             block, off, buffer, size);
00230 }
00231 
00232 static int lfs_bd_cmp(lfs_t *lfs, lfs_block_t block,
00233         lfs_off_t off, const void *buffer, lfs_size_t size) {
00234     return lfs_cache_cmp(lfs, &lfs->rcache, NULL, block, off, buffer, size);
00235 }
00236 
00237 static int lfs_bd_crc(lfs_t *lfs, lfs_block_t block,
00238         lfs_off_t off, lfs_size_t size, uint32_t *crc) {
00239     return lfs_cache_crc(lfs, &lfs->rcache, NULL, block, off, size, crc);
00240 }
00241 
00242 static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
00243     return lfs->cfg->erase(lfs->cfg, block);
00244 }
00245 
00246 static int lfs_bd_sync(lfs_t *lfs) {
00247     lfs->rcache.block = 0xffffffff;
00248 
00249     int err = lfs_cache_flush(lfs, &lfs->pcache, NULL);
00250     if (err) {
00251         return err;
00252     }
00253 
00254     return lfs->cfg->sync(lfs->cfg);
00255 }
00256 
00257 
00258 /// Internal operations predeclared here ///
00259 int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
00260 static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir);
00261 static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
00262         lfs_dir_t *parent, lfs_entry_t *entry);
00263 static int lfs_moved(lfs_t *lfs, const void *e);
00264 static int lfs_relocate(lfs_t *lfs,
00265         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]);
00266 int lfs_deorphan(lfs_t *lfs);
00267 
00268 
00269 /// Block allocator ///
00270 static int lfs_alloc_lookahead(void *p, lfs_block_t block) {
00271     lfs_t *lfs = p;
00272 
00273     lfs_block_t off = (((lfs_soff_t)(block - lfs->free.begin)
00274                 % (lfs_soff_t)(lfs->cfg->block_count))
00275             + lfs->cfg->block_count) % lfs->cfg->block_count;
00276 
00277     if (off < lfs->free.size) {
00278         lfs->free.buffer[off / 32] |= 1U << (off % 32);
00279     }
00280 
00281     return 0;
00282 }
00283 
00284 static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
00285     while (true) {
00286         while (lfs->free.off != lfs->free.size) {
00287             lfs_block_t off = lfs->free.off;
00288             lfs->free.off += 1;
00289 
00290             if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) {
00291                 // found a free block
00292                 *block = (lfs->free.begin + off) % lfs->cfg->block_count;
00293                 return 0;
00294             }
00295         }
00296 
00297         // check if we have looked at all blocks since last ack
00298         if (lfs->free.off == lfs->free.ack - lfs->free.begin) {
00299             LFS_WARN("No more free space %ld", lfs->free.off + lfs->free.begin);
00300             return LFS_ERR_NOSPC;
00301         }
00302 
00303         lfs->free.begin += lfs->free.size;
00304         lfs->free.size = lfs_min(lfs->cfg->lookahead,
00305                 lfs->free.ack - lfs->free.begin);
00306         lfs->free.off = 0;
00307 
00308         // find mask of free blocks from tree
00309         memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
00310         int err = lfs_traverse(lfs, lfs_alloc_lookahead, lfs);
00311         if (err) {
00312             return err;
00313         }
00314     }
00315 }
00316 
00317 static void lfs_alloc_ack(lfs_t *lfs) {
00318     lfs->free.ack = lfs->free.off-1 + lfs->free.begin + lfs->cfg->block_count;
00319 }
00320 
00321 
00322 /// Endian swapping functions ///
00323 static void lfs_dir_fromle32(struct lfs_disk_dir *d) {
00324     d->rev     = lfs_fromle32(d->rev);
00325     d->size    = lfs_fromle32(d->size);
00326     d->tail[0] = lfs_fromle32(d->tail[0]);
00327     d->tail[1] = lfs_fromle32(d->tail[1]);
00328 }
00329 
00330 static void lfs_dir_tole32(struct lfs_disk_dir *d) {
00331     d->rev     = lfs_tole32(d->rev);
00332     d->size    = lfs_tole32(d->size);
00333     d->tail[0] = lfs_tole32(d->tail[0]);
00334     d->tail[1] = lfs_tole32(d->tail[1]);
00335 }
00336 
00337 static void lfs_entry_fromle32(struct lfs_disk_entry *d) {
00338     d->u.dir[0] = lfs_fromle32(d->u.dir[0]);
00339     d->u.dir[1] = lfs_fromle32(d->u.dir[1]);
00340 }
00341 
00342 static void lfs_entry_tole32(struct lfs_disk_entry *d) {
00343     d->u.dir[0] = lfs_tole32(d->u.dir[0]);
00344     d->u.dir[1] = lfs_tole32(d->u.dir[1]);
00345 }
00346 
00347 static void lfs_superblock_fromle32(struct lfs_disk_superblock *d) {
00348     d->root[0]     = lfs_fromle32(d->root[0]);
00349     d->root[1]     = lfs_fromle32(d->root[1]);
00350     d->block_size  = lfs_fromle32(d->block_size);
00351     d->block_count = lfs_fromle32(d->block_count);
00352     d->version     = lfs_fromle32(d->version);
00353 }
00354 
00355 static void lfs_superblock_tole32(struct lfs_disk_superblock *d) {
00356     d->root[0]     = lfs_tole32(d->root[0]);
00357     d->root[1]     = lfs_tole32(d->root[1]);
00358     d->block_size  = lfs_tole32(d->block_size);
00359     d->block_count = lfs_tole32(d->block_count);
00360     d->version     = lfs_tole32(d->version);
00361 }
00362 
00363 
00364 /// Metadata pair and directory operations ///
00365 static inline void lfs_pairswap(lfs_block_t pair[2]) {
00366     lfs_block_t t = pair[0];
00367     pair[0] = pair[1];
00368     pair[1] = t;
00369 }
00370 
00371 static inline bool lfs_pairisnull(const lfs_block_t pair[2]) {
00372     return pair[0] == 0xffffffff || pair[1] == 0xffffffff;
00373 }
00374 
00375 static inline int lfs_paircmp(
00376         const lfs_block_t paira[2],
00377         const lfs_block_t pairb[2]) {
00378     return !(paira[0] == pairb[0] || paira[1] == pairb[1] ||
00379              paira[0] == pairb[1] || paira[1] == pairb[0]);
00380 }
00381 
00382 static inline bool lfs_pairsync(
00383         const lfs_block_t paira[2],
00384         const lfs_block_t pairb[2]) {
00385     return (paira[0] == pairb[0] && paira[1] == pairb[1]) ||
00386            (paira[0] == pairb[1] && paira[1] == pairb[0]);
00387 }
00388 
00389 static inline lfs_size_t lfs_entry_size(const lfs_entry_t *entry) {
00390     return 4 + entry->d.elen + entry->d.alen + entry->d.nlen;
00391 }
00392 
00393 static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) {
00394     // allocate pair of dir blocks
00395     for (int i = 0; i < 2; i++) {
00396         int err = lfs_alloc(lfs, &dir->pair[i]);
00397         if (err) {
00398             return err;
00399         }
00400     }
00401 
00402     // rather than clobbering one of the blocks we just pretend
00403     // the revision may be valid
00404     int err = lfs_bd_read(lfs, dir->pair[0], 0, &dir->d.rev, 4);
00405     dir->d.rev = lfs_fromle32(dir->d.rev);
00406     if (err) {
00407         return err;
00408     }
00409 
00410     // set defaults
00411     dir->d.rev += 1;
00412     dir->d.size = sizeof(dir->d)+4;
00413     dir->d.tail[0] = 0xffffffff;
00414     dir->d.tail[1] = 0xffffffff;
00415     dir->off = sizeof(dir->d);
00416 
00417     // don't write out yet, let caller take care of that
00418     return 0;
00419 }
00420 
00421 static int lfs_dir_fetch(lfs_t *lfs,
00422         lfs_dir_t *dir, const lfs_block_t pair[2]) {
00423     // copy out pair, otherwise may be aliasing dir
00424     const lfs_block_t tpair[2] = {pair[0], pair[1]};
00425     bool valid = false;
00426 
00427     // check both blocks for the most recent revision
00428     for (int i = 0; i < 2; i++) {
00429         struct lfs_disk_dir test;
00430         int err = lfs_bd_read(lfs, tpair[i], 0, &test, sizeof(test));
00431         lfs_dir_fromle32(&test);
00432         if (err) {
00433             return err;
00434         }
00435 
00436         if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) {
00437             continue;
00438         }
00439 
00440         if ((0x7fffffff & test.size) < sizeof(test)+4 ||
00441             (0x7fffffff & test.size) > lfs->cfg->block_size) {
00442             continue;
00443         }
00444 
00445         uint32_t crc = 0xffffffff;
00446         lfs_dir_tole32(&test);
00447         lfs_crc(&crc, &test, sizeof(test));
00448         lfs_dir_fromle32(&test);
00449         err = lfs_bd_crc(lfs, tpair[i], sizeof(test),
00450                 (0x7fffffff & test.size) - sizeof(test), &crc);
00451         if (err) {
00452             return err;
00453         }
00454 
00455         if (crc != 0) {
00456             continue;
00457         }
00458 
00459         valid = true;
00460 
00461         // setup dir in case it's valid
00462         dir->pair[0] = tpair[(i+0) % 2];
00463         dir->pair[1] = tpair[(i+1) % 2];
00464         dir->off = sizeof(dir->d);
00465         dir->d = test;
00466     }
00467 
00468     if (!valid) {
00469         LFS_ERROR("Corrupted dir pair at %ld %ld", tpair[0], tpair[1]);
00470         return LFS_ERR_CORRUPT;
00471     }
00472 
00473     return 0;
00474 }
00475 
00476 struct lfs_region {
00477     lfs_off_t oldoff;
00478     lfs_size_t oldlen;
00479     const void *newdata;
00480     lfs_size_t newlen;
00481 };
00482 
00483 static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
00484         const struct lfs_region *regions, int count) {
00485     // increment revision count
00486     dir->d.rev += 1;
00487 
00488     // keep pairs in order such that pair[0] is most recent
00489     lfs_pairswap(dir->pair);
00490     for (int i = 0; i < count; i++) {
00491         dir->d.size += regions[i].newlen - regions[i].oldlen;
00492     }
00493 
00494     const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]};
00495     bool relocated = false;
00496 
00497     while (true) {
00498         if (true) {
00499             int err = lfs_bd_erase(lfs, dir->pair[0]);
00500             if (err) {
00501                 if (err == LFS_ERR_CORRUPT) {
00502                     goto relocate;
00503                 }
00504                 return err;
00505             }
00506 
00507             uint32_t crc = 0xffffffff;
00508             lfs_dir_tole32(&dir->d);
00509             lfs_crc(&crc, &dir->d, sizeof(dir->d));
00510             err = lfs_bd_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d));
00511             lfs_dir_fromle32(&dir->d);
00512             if (err) {
00513                 if (err == LFS_ERR_CORRUPT) {
00514                     goto relocate;
00515                 }
00516                 return err;
00517             }
00518 
00519             int i = 0;
00520             lfs_off_t oldoff = sizeof(dir->d);
00521             lfs_off_t newoff = sizeof(dir->d);
00522             while (newoff < (0x7fffffff & dir->d.size)-4) {
00523                 if (i < count && regions[i].oldoff == oldoff) {
00524                     lfs_crc(&crc, regions[i].newdata, regions[i].newlen);
00525                     err = lfs_bd_prog(lfs, dir->pair[0],
00526                             newoff, regions[i].newdata, regions[i].newlen);
00527                     if (err) {
00528                         if (err == LFS_ERR_CORRUPT) {
00529                             goto relocate;
00530                         }
00531                         return err;
00532                     }
00533 
00534                     oldoff += regions[i].oldlen;
00535                     newoff += regions[i].newlen;
00536                     i += 1;
00537                 } else {
00538                     uint8_t data;
00539                     err = lfs_bd_read(lfs, oldpair[1], oldoff, &data, 1);
00540                     if (err) {
00541                         return err;
00542                     }
00543 
00544                     lfs_crc(&crc, &data, 1);
00545                     err = lfs_bd_prog(lfs, dir->pair[0], newoff, &data, 1);
00546                     if (err) {
00547                         if (err == LFS_ERR_CORRUPT) {
00548                             goto relocate;
00549                         }
00550                         return err;
00551                     }
00552 
00553                     oldoff += 1;
00554                     newoff += 1;
00555                 }
00556             }
00557 
00558             crc = lfs_tole32(crc);
00559             err = lfs_bd_prog(lfs, dir->pair[0], newoff, &crc, 4);
00560             crc = lfs_fromle32(crc);
00561             if (err) {
00562                 if (err == LFS_ERR_CORRUPT) {
00563                     goto relocate;
00564                 }
00565                 return err;
00566             }
00567 
00568             err = lfs_bd_sync(lfs);
00569             if (err) {
00570                 if (err == LFS_ERR_CORRUPT) {
00571                     goto relocate;
00572                 }
00573                 return err;
00574             }
00575 
00576             // successful commit, check checksum to make sure
00577             uint32_t ncrc = 0xffffffff;
00578             err = lfs_bd_crc(lfs, dir->pair[0], 0,
00579                     (0x7fffffff & dir->d.size)-4, &ncrc);
00580             if (err) {
00581                 return err;
00582             }
00583 
00584             if (ncrc != crc) {
00585                 goto relocate;
00586             }
00587         }
00588 
00589         break;
00590 relocate:
00591         //commit was corrupted
00592         LFS_DEBUG("Bad block at %ld", dir->pair[0]);
00593 
00594         // drop caches and prepare to relocate block
00595         relocated = true;
00596         lfs->pcache.block = 0xffffffff;
00597 
00598         // can't relocate superblock, filesystem is now frozen
00599         if (lfs_paircmp(oldpair, (const lfs_block_t[2]){0, 1}) == 0) {
00600             LFS_WARN("Superblock %ld has become unwritable", oldpair[0]);
00601             return LFS_ERR_CORRUPT;
00602         }
00603 
00604         // relocate half of pair
00605         int err = lfs_alloc(lfs, &dir->pair[0]);
00606         if (err) {
00607             return err;
00608         }
00609     }
00610 
00611     if (relocated) {
00612         // update references if we relocated
00613         LFS_DEBUG("Relocating %ld %ld to %ld %ld",
00614                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]);
00615         int err = lfs_relocate(lfs, oldpair, dir->pair);
00616         if (err) {
00617             return err;
00618         }
00619     }
00620 
00621     // shift over any directories that are affected
00622     for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
00623         if (lfs_paircmp(d->pair, dir->pair) == 0) {
00624             d->pair[0] = dir->pair[0];
00625             d->pair[1] = dir->pair[1];
00626         }
00627     }
00628 
00629     return 0;
00630 }
00631 
00632 static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
00633         lfs_entry_t *entry, const void *data) {
00634     lfs_entry_tole32(&entry->d);
00635     int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
00636             {entry->off, sizeof(entry->d), &entry->d, sizeof(entry->d)},
00637             {entry->off+sizeof(entry->d), entry->d.nlen, data, entry->d.nlen}
00638         }, data ? 2 : 1);
00639     lfs_entry_fromle32(&entry->d);
00640     return err;
00641 }
00642 
00643 static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
00644         lfs_entry_t *entry, const void *data) {
00645     // check if we fit, if top bit is set we do not and move on
00646     while (true) {
00647         if (dir->d.size + lfs_entry_size(entry) <= lfs->cfg->block_size) {
00648             entry->off = dir->d.size - 4;
00649 
00650             lfs_entry_tole32(&entry->d);
00651             int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
00652                     {entry->off, 0, &entry->d, sizeof(entry->d)},
00653                     {entry->off, 0, data, entry->d.nlen}
00654                 }, 2);
00655             lfs_entry_fromle32(&entry->d);
00656             return err;
00657         }
00658 
00659         // we need to allocate a new dir block
00660         if (!(0x80000000 & dir->d.size)) {
00661             lfs_dir_t olddir = *dir;
00662             int err = lfs_dir_alloc(lfs, dir);
00663             if (err) {
00664                 return err;
00665             }
00666 
00667             dir->d.tail[0] = olddir.d.tail[0];
00668             dir->d.tail[1] = olddir.d.tail[1];
00669             entry->off = dir->d.size - 4;
00670             lfs_entry_tole32(&entry->d);
00671             err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
00672                     {entry->off, 0, &entry->d, sizeof(entry->d)},
00673                     {entry->off, 0, data, entry->d.nlen}
00674                 }, 2);
00675             lfs_entry_fromle32(&entry->d);
00676             if (err) {
00677                 return err;
00678             }
00679 
00680             olddir.d.size |= 0x80000000;
00681             olddir.d.tail[0] = dir->pair[0];
00682             olddir.d.tail[1] = dir->pair[1];
00683             return lfs_dir_commit(lfs, &olddir, NULL, 0);
00684         }
00685 
00686         int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
00687         if (err) {
00688             return err;
00689         }
00690     }
00691 }
00692 
00693 static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
00694     // check if we should just drop the directory block
00695     if ((dir->d.size & 0x7fffffff) == sizeof(dir->d)+4
00696             + lfs_entry_size(entry)) {
00697         lfs_dir_t pdir;
00698         int res = lfs_pred(lfs, dir->pair, &pdir);
00699         if (res < 0) {
00700             return res;
00701         }
00702 
00703         if (pdir.d.size & 0x80000000) {
00704             pdir.d.size &= dir->d.size | 0x7fffffff;
00705             pdir.d.tail[0] = dir->d.tail[0];
00706             pdir.d.tail[1] = dir->d.tail[1];
00707             return lfs_dir_commit(lfs, &pdir, NULL, 0);
00708         }
00709     }
00710 
00711     // shift out the entry
00712     int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){
00713             {entry->off, lfs_entry_size(entry), NULL, 0},
00714         }, 1);
00715     if (err) {
00716         return err;
00717     }
00718 
00719     // shift over any files/directories that are affected
00720     for (lfs_file_t *f = lfs->files; f; f = f->next) {
00721         if (lfs_paircmp(f->pair, dir->pair) == 0) {
00722             if (f->poff == entry->off) {
00723                 f->pair[0] = 0xffffffff;
00724                 f->pair[1] = 0xffffffff;
00725             } else if (f->poff > entry->off) {
00726                 f->poff -= lfs_entry_size(entry);
00727             }
00728         }
00729     }
00730 
00731     for (lfs_dir_t *d = lfs->dirs; d; d = d->next) {
00732         if (lfs_paircmp(d->pair, dir->pair) == 0) {
00733             if (d->off > entry->off) {
00734                 d->off -= lfs_entry_size(entry);
00735                 d->pos -= lfs_entry_size(entry);
00736             }
00737         }
00738     }
00739 
00740     return 0;
00741 }
00742 
00743 static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
00744     while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) {
00745         if (!(0x80000000 & dir->d.size)) {
00746             entry->off = dir->off;
00747             return LFS_ERR_NOENT;
00748         }
00749 
00750         int err = lfs_dir_fetch(lfs, dir, dir->d.tail);
00751         if (err) {
00752             return err;
00753         }
00754 
00755         dir->off = sizeof(dir->d);
00756         dir->pos += sizeof(dir->d) + 4;
00757     }
00758 
00759     int err = lfs_bd_read(lfs, dir->pair[0], dir->off,
00760             &entry->d, sizeof(entry->d));
00761     lfs_entry_fromle32(&entry->d);
00762     if (err) {
00763         return err;
00764     }
00765 
00766     entry->off = dir->off;
00767     dir->off += lfs_entry_size(entry);
00768     dir->pos += lfs_entry_size(entry);
00769     return 0;
00770 }
00771 
00772 static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir,
00773         lfs_entry_t *entry, const char **path) {
00774     const char *pathname = *path;
00775     size_t pathlen;
00776 
00777     while (true) {
00778     nextname:
00779         // skip slashes
00780         pathname += strspn(pathname, "/");
00781         pathlen = strcspn(pathname, "/");
00782 
00783         // special case for root dir
00784         if (pathname[0] == '\0') {
00785             *entry = (lfs_entry_t){
00786                 .d.type = LFS_TYPE_DIR,
00787                 .d.elen = sizeof(entry->d) - 4,
00788                 .d.alen = 0,
00789                 .d.nlen = 0,
00790                 .d.u.dir[0] = lfs->root[0],
00791                 .d.u.dir[1] = lfs->root[1],
00792             };
00793             return 0;
00794         }
00795 
00796         // skip '.' and root '..'
00797         if ((pathlen == 1 && memcmp(pathname, ".", 1) == 0) ||
00798             (pathlen == 2 && memcmp(pathname, "..", 2) == 0)) {
00799             pathname += pathlen;
00800             goto nextname;
00801         }
00802 
00803         // skip if matched by '..' in name
00804         const char *suffix = pathname + pathlen;
00805         size_t sufflen;
00806         int depth = 1;
00807         while (true) {
00808             suffix += strspn(suffix, "/");
00809             sufflen = strcspn(suffix, "/");
00810             if (sufflen == 0) {
00811                 break;
00812             }
00813 
00814             if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
00815                 depth -= 1;
00816                 if (depth == 0) {
00817                     pathname = suffix + sufflen;
00818                     goto nextname;
00819                 }
00820             } else {
00821                 depth += 1;
00822             }
00823 
00824             suffix += sufflen;
00825         }
00826 
00827         // update what we've found
00828         *path = pathname;
00829 
00830         // find path
00831         while (true) {
00832             int err = lfs_dir_next(lfs, dir, entry);
00833             if (err) {
00834                 return err;
00835             }
00836 
00837             if (((0x7f & entry->d.type) != LFS_TYPE_REG &&
00838                  (0x7f & entry->d.type) != LFS_TYPE_DIR) ||
00839                 entry->d.nlen != pathlen) {
00840                 continue;
00841             }
00842 
00843             int res = lfs_bd_cmp(lfs, dir->pair[0],
00844                     entry->off + 4+entry->d.elen+entry->d.alen,
00845                     pathname, pathlen);
00846             if (res < 0) {
00847                 return res;
00848             }
00849 
00850             // found match
00851             if (res) {
00852                 break;
00853             }
00854         }
00855 
00856         // check that entry has not been moved
00857         if (entry->d.type & 0x80) {
00858             int moved = lfs_moved(lfs, &entry->d.u);
00859             if (moved < 0 || moved) {
00860                 return (moved < 0) ? moved : LFS_ERR_NOENT;
00861             }
00862 
00863             entry->d.type &= ~0x80;
00864         }
00865 
00866         pathname += pathlen;
00867         pathname += strspn(pathname, "/");
00868         if (pathname[0] == '\0') {
00869             return 0;
00870         }
00871 
00872         // continue on if we hit a directory
00873         if (entry->d.type != LFS_TYPE_DIR) {
00874             return LFS_ERR_NOTDIR;
00875         }
00876 
00877         int err = lfs_dir_fetch(lfs, dir, entry->d.u.dir);
00878         if (err) {
00879             return err;
00880         }
00881     }
00882 }
00883 
00884 
00885 /// Top level directory operations ///
00886 int lfs_mkdir(lfs_t *lfs, const char *path) {
00887     // deorphan if we haven't yet, needed at most once after poweron
00888     if (!lfs->deorphaned) {
00889         int err = lfs_deorphan(lfs);
00890         if (err) {
00891             return err;
00892         }
00893     }
00894 
00895     // fetch parent directory
00896     lfs_dir_t cwd;
00897     int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
00898     if (err) {
00899         return err;
00900     }
00901 
00902     lfs_entry_t entry;
00903     err = lfs_dir_find(lfs, &cwd, &entry, &path);
00904     if (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) {
00905         return err ? err : LFS_ERR_EXIST;
00906     }
00907 
00908     // build up new directory
00909     lfs_alloc_ack(lfs);
00910 
00911     lfs_dir_t dir;
00912     err = lfs_dir_alloc(lfs, &dir);
00913     if (err) {
00914         return err;
00915     }
00916     dir.d.tail[0] = cwd.d.tail[0];
00917     dir.d.tail[1] = cwd.d.tail[1];
00918 
00919     err = lfs_dir_commit(lfs, &dir, NULL, 0);
00920     if (err) {
00921         return err;
00922     }
00923 
00924     entry.d.type = LFS_TYPE_DIR;
00925     entry.d.elen = sizeof(entry.d) - 4;
00926     entry.d.alen = 0;
00927     entry.d.nlen = strlen(path);
00928     entry.d.u.dir[0] = dir.pair[0];
00929     entry.d.u.dir[1] = dir.pair[1];
00930 
00931     cwd.d.tail[0] = dir.pair[0];
00932     cwd.d.tail[1] = dir.pair[1];
00933 
00934     err = lfs_dir_append(lfs, &cwd, &entry, path);
00935     if (err) {
00936         return err;
00937     }
00938 
00939     lfs_alloc_ack(lfs);
00940     return 0;
00941 }
00942 
00943 int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
00944     dir->pair[0] = lfs->root[0];
00945     dir->pair[1] = lfs->root[1];
00946 
00947     int err = lfs_dir_fetch(lfs, dir, dir->pair);
00948     if (err) {
00949         return err;
00950     }
00951 
00952     lfs_entry_t entry;
00953     err = lfs_dir_find(lfs, dir, &entry, &path);
00954     if (err) {
00955         return err;
00956     } else if (entry.d.type != LFS_TYPE_DIR) {
00957         return LFS_ERR_NOTDIR;
00958     }
00959 
00960     err = lfs_dir_fetch(lfs, dir, entry.d.u.dir);
00961     if (err) {
00962         return err;
00963     }
00964 
00965     // setup head dir
00966     // special offset for '.' and '..'
00967     dir->head[0] = dir->pair[0];
00968     dir->head[1] = dir->pair[1];
00969     dir->pos = sizeof(dir->d) - 2;
00970     dir->off = sizeof(dir->d);
00971 
00972     // add to list of directories
00973     dir->next = lfs->dirs;
00974     lfs->dirs = dir;
00975 
00976     return 0;
00977 }
00978 
00979 int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
00980     // remove from list of directories
00981     for (lfs_dir_t **p = &lfs->dirs; *p; p = &(*p)->next) {
00982         if (*p == dir) {
00983             *p = dir->next;
00984             break;
00985         }
00986     }
00987 
00988     return 0;
00989 }
00990 
00991 int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
00992     memset(info, 0, sizeof(*info));
00993 
00994     // special offset for '.' and '..'
00995     if (dir->pos == sizeof(dir->d) - 2) {
00996         info->type = LFS_TYPE_DIR;
00997         strcpy(info->name, ".");
00998         dir->pos += 1;
00999         return 1;
01000     } else if (dir->pos == sizeof(dir->d) - 1) {
01001         info->type = LFS_TYPE_DIR;
01002         strcpy(info->name, "..");
01003         dir->pos += 1;
01004         return 1;
01005     }
01006 
01007     lfs_entry_t entry;
01008     while (true) {
01009         int err = lfs_dir_next(lfs, dir, &entry);
01010         if (err) {
01011             return (err == LFS_ERR_NOENT) ? 0 : err;
01012         }
01013 
01014         if ((0x7f & entry.d.type) != LFS_TYPE_REG &&
01015             (0x7f & entry.d.type) != LFS_TYPE_DIR) {
01016             continue;
01017         }
01018 
01019         // check that entry has not been moved
01020         if (entry.d.type & 0x80) {
01021             int moved = lfs_moved(lfs, &entry.d.u);
01022             if (moved < 0) {
01023                 return moved;
01024             }
01025 
01026             if (moved) {
01027                 continue;
01028             }
01029 
01030             entry.d.type &= ~0x80;
01031         }
01032 
01033         break;
01034     }
01035 
01036     info->type = entry.d.type;
01037     if (info->type == LFS_TYPE_REG) {
01038         info->size = entry.d.u.file.size;
01039     }
01040 
01041     int err = lfs_bd_read(lfs, dir->pair[0],
01042             entry.off + 4+entry.d.elen+entry.d.alen,
01043             info->name, entry.d.nlen);
01044     if (err) {
01045         return err;
01046     }
01047 
01048     return 1;
01049 }
01050 
01051 int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
01052     // simply walk from head dir
01053     int err = lfs_dir_rewind(lfs, dir);
01054     if (err) {
01055         return err;
01056     }
01057     dir->pos = off;
01058 
01059     while (off > (0x7fffffff & dir->d.size)) {
01060         off -= 0x7fffffff & dir->d.size;
01061         if (!(0x80000000 & dir->d.size)) {
01062             return LFS_ERR_INVAL;
01063         }
01064 
01065         err = lfs_dir_fetch(lfs, dir, dir->d.tail);
01066         if (err) {
01067             return err;
01068         }
01069     }
01070 
01071     dir->off = off;
01072     return 0;
01073 }
01074 
01075 lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) {
01076     (void)lfs;
01077     return dir->pos;
01078 }
01079 
01080 int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
01081     // reload the head dir
01082     int err = lfs_dir_fetch(lfs, dir, dir->head);
01083     if (err) {
01084         return err;
01085     }
01086 
01087     dir->pair[0] = dir->head[0];
01088     dir->pair[1] = dir->head[1];
01089     dir->pos = sizeof(dir->d) - 2;
01090     dir->off = sizeof(dir->d);
01091     return 0;
01092 }
01093 
01094 
01095 /// File index list operations ///
01096 static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) {
01097     lfs_off_t size = *off;
01098     lfs_off_t b = lfs->cfg->block_size - 2*4;
01099     lfs_off_t i = size / b;
01100     if (i == 0) {
01101         return 0;
01102     }
01103 
01104     i = (size - 4*(lfs_popc(i-1)+2)) / b;
01105     *off = size - b*i - 4*lfs_popc(i);
01106     return i;
01107 }
01108 
01109 static int lfs_ctz_find(lfs_t *lfs,
01110         lfs_cache_t *rcache, const lfs_cache_t *pcache,
01111         lfs_block_t head, lfs_size_t size,
01112         lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) {
01113     if (size == 0) {
01114         *block = 0xffffffff;
01115         *off = 0;
01116         return 0;
01117     }
01118 
01119     lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1});
01120     lfs_off_t target = lfs_ctz_index(lfs, &pos);
01121 
01122     while (current > target) {
01123         lfs_size_t skip = lfs_min(
01124                 lfs_npw2(current-target+1) - 1,
01125                 lfs_ctz(current));
01126 
01127         int err = lfs_cache_read(lfs, rcache, pcache, head, 4*skip, &head, 4);
01128         head = lfs_fromle32(head);
01129         if (err) {
01130             return err;
01131         }
01132 
01133         LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
01134         current -= 1 << skip;
01135     }
01136 
01137     *block = head;
01138     *off = pos;
01139     return 0;
01140 }
01141 
01142 static int lfs_ctz_extend(lfs_t *lfs,
01143         lfs_cache_t *rcache, lfs_cache_t *pcache,
01144         lfs_block_t head, lfs_size_t size,
01145         lfs_block_t *block, lfs_off_t *off) {
01146     while (true) {
01147         // go ahead and grab a block
01148         lfs_block_t nblock;
01149         int err = lfs_alloc(lfs, &nblock);
01150         if (err) {
01151             return err;
01152         }
01153         LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);
01154 
01155         if (true) {
01156             err = lfs_bd_erase(lfs, nblock);
01157             if (err) {
01158                 if (err == LFS_ERR_CORRUPT) {
01159                     goto relocate;
01160                 }
01161                 return err;
01162             }
01163 
01164             if (size == 0) {
01165                 *block = nblock;
01166                 *off = 0;
01167                 return 0;
01168             }
01169 
01170             size -= 1;
01171             lfs_off_t index = lfs_ctz_index(lfs, &size);
01172             size += 1;
01173 
01174             // just copy out the last block if it is incomplete
01175             if (size != lfs->cfg->block_size) {
01176                 for (lfs_off_t i = 0; i < size; i++) {
01177                     uint8_t data;
01178                     err = lfs_cache_read(lfs, rcache, NULL,
01179                             head, i, &data, 1);
01180                     if (err) {
01181                         return err;
01182                     }
01183 
01184                     err = lfs_cache_prog(lfs, pcache, rcache,
01185                             nblock, i, &data, 1);
01186                     if (err) {
01187                         if (err == LFS_ERR_CORRUPT) {
01188                             goto relocate;
01189                         }
01190                         return err;
01191                     }
01192                 }
01193 
01194                 *block = nblock;
01195                 *off = size;
01196                 return 0;
01197             }
01198 
01199             // append block
01200             index += 1;
01201             lfs_size_t skips = lfs_ctz(index) + 1;
01202 
01203             for (lfs_off_t i = 0; i < skips; i++) {
01204                 head = lfs_tole32(head);
01205                 err = lfs_cache_prog(lfs, pcache, rcache,
01206                         nblock, 4*i, &head, 4);
01207                 head = lfs_fromle32(head);
01208                 if (err) {
01209                     if (err == LFS_ERR_CORRUPT) {
01210                         goto relocate;
01211                     }
01212                     return err;
01213                 }
01214 
01215                 if (i != skips-1) {
01216                     err = lfs_cache_read(lfs, rcache, NULL,
01217                             head, 4*i, &head, 4);
01218                     head = lfs_fromle32(head);
01219                     if (err) {
01220                         return err;
01221                     }
01222                 }
01223 
01224                 LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
01225             }
01226 
01227             *block = nblock;
01228             *off = 4*skips;
01229             return 0;
01230         }
01231 
01232 relocate:
01233         LFS_DEBUG("Bad block at %ld", nblock);
01234 
01235         // just clear cache and try a new block
01236         pcache->block = 0xffffffff;
01237     }
01238 }
01239 
01240 static int lfs_ctz_traverse(lfs_t *lfs,
01241         lfs_cache_t *rcache, const lfs_cache_t *pcache,
01242         lfs_block_t head, lfs_size_t size,
01243         int (*cb)(void*, lfs_block_t), void *data) {
01244     if (size == 0) {
01245         return 0;
01246     }
01247 
01248     lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1});
01249 
01250     while (true) {
01251         int err = cb(data, head);
01252         if (err) {
01253             return err;
01254         }
01255 
01256         if (index == 0) {
01257             return 0;
01258         }
01259 
01260         lfs_block_t heads[2];
01261         int count = 2 - (index & 1);
01262         err = lfs_cache_read(lfs, rcache, pcache, head, 0, &heads, count*4);
01263         heads[0] = lfs_fromle32(heads[0]);
01264         heads[1] = lfs_fromle32(heads[1]);
01265         if (err) {
01266             return err;
01267         }
01268 
01269         for (int i = 0; i < count-1; i++) {
01270             err = cb(data, heads[i]);
01271             if (err) {
01272                 return err;
01273             }
01274         }
01275 
01276         head = heads[count-1];
01277         index -= count;
01278     }
01279 }
01280 
01281 
01282 /// Top level file operations ///
01283 int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
01284         const char *path, int flags) {
01285     // deorphan if we haven't yet, needed at most once after poweron
01286     if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) {
01287         int err = lfs_deorphan(lfs);
01288         if (err) {
01289             return err;
01290         }
01291     }
01292 
01293     // allocate entry for file if it doesn't exist
01294     lfs_dir_t cwd;
01295     int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
01296     if (err) {
01297         return err;
01298     }
01299 
01300     lfs_entry_t entry;
01301     err = lfs_dir_find(lfs, &cwd, &entry, &path);
01302     if (err && (err != LFS_ERR_NOENT || strchr(path, '/') != NULL)) {
01303         return err;
01304     }
01305 
01306     if (err == LFS_ERR_NOENT) {
01307         if (!(flags & LFS_O_CREAT)) {
01308             return LFS_ERR_NOENT;
01309         }
01310 
01311         // create entry to remember name
01312         entry.d.type = LFS_TYPE_REG;
01313         entry.d.elen = sizeof(entry.d) - 4;
01314         entry.d.alen = 0;
01315         entry.d.nlen = strlen(path);
01316         entry.d.u.file.head = 0xffffffff;
01317         entry.d.u.file.size = 0;
01318         err = lfs_dir_append(lfs, &cwd, &entry, path);
01319         if (err) {
01320             return err;
01321         }
01322     } else if (entry.d.type == LFS_TYPE_DIR) {
01323         return LFS_ERR_ISDIR;
01324     } else if (flags & LFS_O_EXCL) {
01325         return LFS_ERR_EXIST;
01326     }
01327 
01328     // setup file struct
01329     file->pair[0] = cwd.pair[0];
01330     file->pair[1] = cwd.pair[1];
01331     file->poff = entry.off;
01332     file->head = entry.d.u.file.head;
01333     file->size = entry.d.u.file.size;
01334     file->flags = flags;
01335     file->pos = 0;
01336 
01337     if (flags & LFS_O_TRUNC) {
01338         if (file->size != 0) {
01339             file->flags |= LFS_F_DIRTY;
01340         }
01341         file->head = 0xffffffff;
01342         file->size = 0;
01343     }
01344 
01345     // allocate buffer if needed
01346     file->cache.block = 0xffffffff;
01347     if (lfs->cfg->file_buffer) {
01348         file->cache.buffer = lfs->cfg->file_buffer;
01349     } else if ((file->flags & 3) == LFS_O_RDONLY) {
01350         file->cache.buffer = lfs_malloc(lfs->cfg->read_size);
01351         if (!file->cache.buffer) {
01352             return LFS_ERR_NOMEM;
01353         }
01354     } else {
01355         file->cache.buffer = lfs_malloc(lfs->cfg->prog_size);
01356         if (!file->cache.buffer) {
01357             return LFS_ERR_NOMEM;
01358         }
01359     }
01360 
01361     // add to list of files
01362     file->next = lfs->files;
01363     lfs->files = file;
01364 
01365     return 0;
01366 }
01367 
01368 int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
01369     int err = lfs_file_sync(lfs, file);
01370 
01371     // remove from list of files
01372     for (lfs_file_t **p = &lfs->files; *p; p = &(*p)->next) {
01373         if (*p == file) {
01374             *p = file->next;
01375             break;
01376         }
01377     }
01378 
01379     // clean up memory
01380     if (!lfs->cfg->file_buffer) {
01381         lfs_free(file->cache.buffer);
01382     }
01383 
01384     return err;
01385 }
01386 
01387 static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
01388 relocate:
01389     LFS_DEBUG("Bad block at %ld", file->block);
01390 
01391     // just relocate what exists into new block
01392     lfs_block_t nblock;
01393     int err = lfs_alloc(lfs, &nblock);
01394     if (err) {
01395         return err;
01396     }
01397 
01398     err = lfs_bd_erase(lfs, nblock);
01399     if (err) {
01400         if (err == LFS_ERR_CORRUPT) {
01401             goto relocate;
01402         }
01403         return err;
01404     }
01405 
01406     // either read from dirty cache or disk
01407     for (lfs_off_t i = 0; i < file->off; i++) {
01408         uint8_t data;
01409         err = lfs_cache_read(lfs, &lfs->rcache, &file->cache,
01410                 file->block, i, &data, 1);
01411         if (err) {
01412             return err;
01413         }
01414 
01415         err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache,
01416                 nblock, i, &data, 1);
01417         if (err) {
01418             if (err == LFS_ERR_CORRUPT) {
01419                 goto relocate;
01420             }
01421             return err;
01422         }
01423     }
01424 
01425     // copy over new state of file
01426     memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size);
01427     file->cache.block = lfs->pcache.block;
01428     file->cache.off = lfs->pcache.off;
01429     lfs->pcache.block = 0xffffffff;
01430 
01431     file->block = nblock;
01432     return 0;
01433 }
01434 
01435 static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
01436     if (file->flags & LFS_F_READING) {
01437         // just drop read cache
01438         file->cache.block = 0xffffffff;
01439         file->flags &= ~LFS_F_READING;
01440     }
01441 
01442     if (file->flags & LFS_F_WRITING) {
01443         lfs_off_t pos = file->pos;
01444 
01445         // copy over anything after current branch
01446         lfs_file_t orig = {
01447             .head = file->head,
01448             .size = file->size,
01449             .flags = LFS_O_RDONLY,
01450             .pos = file->pos,
01451             .cache = lfs->rcache,
01452         };
01453         lfs->rcache.block = 0xffffffff;
01454 
01455         while (file->pos < file->size) {
01456             // copy over a byte at a time, leave it up to caching
01457             // to make this efficient
01458             uint8_t data;
01459             lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1);
01460             if (res < 0) {
01461                 return res;
01462             }
01463 
01464             res = lfs_file_write(lfs, file, &data, 1);
01465             if (res < 0) {
01466                 return res;
01467             }
01468 
01469             // keep our reference to the rcache in sync
01470             if (lfs->rcache.block != 0xffffffff) {
01471                 orig.cache.block = 0xffffffff;
01472                 lfs->rcache.block = 0xffffffff;
01473             }
01474         }
01475 
01476         // write out what we have
01477         while (true) {
01478             int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache);
01479             if (err) {
01480                 if (err == LFS_ERR_CORRUPT) {
01481                     goto relocate;
01482                 }
01483                 return err;
01484             }
01485 
01486             break;
01487 relocate:
01488             err = lfs_file_relocate(lfs, file);
01489             if (err) {
01490                 return err;
01491             }
01492         }
01493 
01494         // actual file updates
01495         file->head = file->block;
01496         file->size = file->pos;
01497         file->flags &= ~LFS_F_WRITING;
01498         file->flags |= LFS_F_DIRTY;
01499 
01500         file->pos = pos;
01501     }
01502 
01503     return 0;
01504 }
01505 
01506 int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
01507     int err = lfs_file_flush(lfs, file);
01508     if (err) {
01509         return err;
01510     }
01511 
01512     if ((file->flags & LFS_F_DIRTY) &&
01513             !(file->flags & LFS_F_ERRED) &&
01514             !lfs_pairisnull(file->pair)) {
01515         // update dir entry
01516         lfs_dir_t cwd;
01517         err = lfs_dir_fetch(lfs, &cwd, file->pair);
01518         if (err) {
01519             return err;
01520         }
01521 
01522         lfs_entry_t entry = {.off = file->poff};
01523         err = lfs_bd_read(lfs, cwd.pair[0], entry.off,
01524                 &entry.d, sizeof(entry.d));
01525         lfs_entry_fromle32(&entry.d);
01526         if (err) {
01527             return err;
01528         }
01529 
01530         LFS_ASSERT(entry.d.type == LFS_TYPE_REG);
01531         entry.d.u.file.head = file->head;
01532         entry.d.u.file.size = file->size;
01533 
01534         err = lfs_dir_update(lfs, &cwd, &entry, NULL);
01535         if (err) {
01536             return err;
01537         }
01538 
01539         file->flags &= ~LFS_F_DIRTY;
01540     }
01541 
01542     return 0;
01543 }
01544 
01545 lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
01546         void *buffer, lfs_size_t size) {
01547     uint8_t *data = buffer;
01548     lfs_size_t nsize = size;
01549 
01550     if ((file->flags & 3) == LFS_O_WRONLY) {
01551         return LFS_ERR_BADF;
01552     }
01553 
01554     if (file->flags & LFS_F_WRITING) {
01555         // flush out any writes
01556         int err = lfs_file_flush(lfs, file);
01557         if (err) {
01558             return err;
01559         }
01560     }
01561 
01562     if (file->pos >= file->size) {
01563         // eof if past end
01564         return 0;
01565     }
01566 
01567     size = lfs_min(size, file->size - file->pos);
01568     nsize = size;
01569 
01570     while (nsize > 0) {
01571         // check if we need a new block
01572         if (!(file->flags & LFS_F_READING) ||
01573                 file->off == lfs->cfg->block_size) {
01574             int err = lfs_ctz_find(lfs, &file->cache, NULL,
01575                     file->head, file->size,
01576                     file->pos, &file->block, &file->off);
01577             if (err) {
01578                 return err;
01579             }
01580 
01581             file->flags |= LFS_F_READING;
01582         }
01583 
01584         // read as much as we can in current block
01585         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
01586         int err = lfs_cache_read(lfs, &file->cache, NULL,
01587                 file->block, file->off, data, diff);
01588         if (err) {
01589             return err;
01590         }
01591 
01592         file->pos += diff;
01593         file->off += diff;
01594         data += diff;
01595         nsize -= diff;
01596     }
01597 
01598     return size;
01599 }
01600 
01601 lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
01602         const void *buffer, lfs_size_t size) {
01603     const uint8_t *data = buffer;
01604     lfs_size_t nsize = size;
01605 
01606     if ((file->flags & 3) == LFS_O_RDONLY) {
01607         return LFS_ERR_BADF;
01608     }
01609 
01610     if (file->flags & LFS_F_READING) {
01611         // drop any reads
01612         int err = lfs_file_flush(lfs, file);
01613         if (err) {
01614             return err;
01615         }
01616     }
01617 
01618     if ((file->flags & LFS_O_APPEND) && file->pos < file->size) {
01619         file->pos = file->size;
01620     }
01621 
01622     if (!(file->flags & LFS_F_WRITING) && file->pos > file->size) {
01623         // fill with zeros
01624         lfs_off_t pos = file->pos;
01625         file->pos = file->size;
01626 
01627         while (file->pos < pos) {
01628             lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1);
01629             if (res < 0) {
01630                 return res;
01631             }
01632         }
01633     }
01634 
01635     while (nsize > 0) {
01636         // check if we need a new block
01637         if (!(file->flags & LFS_F_WRITING) ||
01638                 file->off == lfs->cfg->block_size) {
01639             if (!(file->flags & LFS_F_WRITING) && file->pos > 0) {
01640                 // find out which block we're extending from
01641                 int err = lfs_ctz_find(lfs, &file->cache, NULL,
01642                         file->head, file->size,
01643                         file->pos-1, &file->block, &file->off);
01644                 if (err) {
01645                     file->flags |= LFS_F_ERRED;
01646                     return err;
01647                 }
01648 
01649                 // mark cache as dirty since we may have read data into it
01650                 file->cache.block = 0xffffffff;
01651             }
01652 
01653             // extend file with new blocks
01654             lfs_alloc_ack(lfs);
01655             int err = lfs_ctz_extend(lfs, &lfs->rcache, &file->cache,
01656                     file->block, file->pos,
01657                     &file->block, &file->off);
01658             if (err) {
01659                 file->flags |= LFS_F_ERRED;
01660                 return err;
01661             }
01662 
01663             file->flags |= LFS_F_WRITING;
01664         }
01665 
01666         // program as much as we can in current block
01667         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
01668         while (true) {
01669             int err = lfs_cache_prog(lfs, &file->cache, &lfs->rcache,
01670                     file->block, file->off, data, diff);
01671             if (err) {
01672                 if (err == LFS_ERR_CORRUPT) {
01673                     goto relocate;
01674                 }
01675                 file->flags |= LFS_F_ERRED;
01676                 return err;
01677             }
01678 
01679             break;
01680 relocate:
01681             err = lfs_file_relocate(lfs, file);
01682             if (err) {
01683                 file->flags |= LFS_F_ERRED;
01684                 return err;
01685             }
01686         }
01687 
01688         file->pos += diff;
01689         file->off += diff;
01690         data += diff;
01691         nsize -= diff;
01692 
01693         lfs_alloc_ack(lfs);
01694     }
01695 
01696     file->flags &= ~LFS_F_ERRED;
01697     return size;
01698 }
01699 
01700 lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
01701         lfs_soff_t off, int whence) {
01702     // write out everything beforehand, may be noop if rdonly
01703     int err = lfs_file_flush(lfs, file);
01704     if (err) {
01705         return err;
01706     }
01707 
01708     // update pos
01709     if (whence == LFS_SEEK_SET) {
01710         file->pos = off;
01711     } else if (whence == LFS_SEEK_CUR) {
01712         if (off < 0 && (lfs_off_t)-off > file->pos) {
01713             return LFS_ERR_INVAL;
01714         }
01715 
01716         file->pos = file->pos + off;
01717     } else if (whence == LFS_SEEK_END) {
01718         if (off < 0 && (lfs_off_t)-off > file->size) {
01719             return LFS_ERR_INVAL;
01720         }
01721 
01722         file->pos = file->size + off;
01723     }
01724 
01725     return file->pos;
01726 }
01727 
01728 int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
01729     if ((file->flags & 3) == LFS_O_RDONLY) {
01730         return LFS_ERR_BADF;
01731     }
01732 
01733     lfs_off_t oldsize = lfs_file_size(lfs, file);
01734     if (size < oldsize) {
01735         // need to flush since directly changing metadata
01736         int err = lfs_file_flush(lfs, file);
01737         if (err) {
01738             return err;
01739         }
01740 
01741         // lookup new head in ctz skip list
01742         err = lfs_ctz_find(lfs, &file->cache, NULL,
01743                 file->head, file->size,
01744                 size, &file->head, &(lfs_off_t){0});
01745         if (err) {
01746             return err;
01747         }
01748 
01749         file->size = size;
01750         file->flags |= LFS_F_DIRTY;
01751     } else if (size > oldsize) {
01752         lfs_off_t pos = file->pos;
01753 
01754         // flush+seek if not already at end
01755         if (file->pos != oldsize) {
01756             int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
01757             if (err < 0) {
01758                 return err;
01759             }
01760         }
01761 
01762         // fill with zeros
01763         while (file->pos < size) {
01764             lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1);
01765             if (res < 0) {
01766                 return res;
01767             }
01768         }
01769 
01770         // restore pos
01771         int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET);
01772         if (err < 0) {
01773             return err;
01774         }
01775     }
01776 
01777     return 0;
01778 }
01779 
01780 lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) {
01781     (void)lfs;
01782     return file->pos;
01783 }
01784 
01785 int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
01786     lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET);
01787     if (res < 0) {
01788         return res;
01789     }
01790 
01791     return 0;
01792 }
01793 
01794 lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
01795     (void)lfs;
01796     if (file->flags & LFS_F_WRITING) {
01797         return lfs_max(file->pos, file->size);
01798     } else {
01799         return file->size;
01800     }
01801 }
01802 
01803 
01804 /// General fs operations ///
01805 int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
01806     lfs_dir_t cwd;
01807     int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
01808     if (err) {
01809         return err;
01810     }
01811 
01812     lfs_entry_t entry;
01813     err = lfs_dir_find(lfs, &cwd, &entry, &path);
01814     if (err) {
01815         return err;
01816     }
01817 
01818     memset(info, 0, sizeof(*info));
01819     info->type = entry.d.type;
01820     if (info->type == LFS_TYPE_REG) {
01821         info->size = entry.d.u.file.size;
01822     }
01823 
01824     if (lfs_paircmp(entry.d.u.dir, lfs->root) == 0) {
01825         strcpy(info->name, "/");
01826     } else {
01827         err = lfs_bd_read(lfs, cwd.pair[0],
01828                 entry.off + 4+entry.d.elen+entry.d.alen,
01829                 info->name, entry.d.nlen);
01830         if (err) {
01831             return err;
01832         }
01833     }
01834 
01835     return 0;
01836 }
01837 
01838 int lfs_remove(lfs_t *lfs, const char *path) {
01839     // deorphan if we haven't yet, needed at most once after poweron
01840     if (!lfs->deorphaned) {
01841         int err = lfs_deorphan(lfs);
01842         if (err) {
01843             return err;
01844         }
01845     }
01846 
01847     lfs_dir_t cwd;
01848     int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
01849     if (err) {
01850         return err;
01851     }
01852 
01853     lfs_entry_t entry;
01854     err = lfs_dir_find(lfs, &cwd, &entry, &path);
01855     if (err) {
01856         return err;
01857     }
01858 
01859     lfs_dir_t dir;
01860     if (entry.d.type == LFS_TYPE_DIR) {
01861         // must be empty before removal, checking size
01862         // without masking top bit checks for any case where
01863         // dir is not empty
01864         err = lfs_dir_fetch(lfs, &dir, entry.d.u.dir);
01865         if (err) {
01866             return err;
01867         } else if (dir.d.size != sizeof(dir.d)+4) {
01868             return LFS_ERR_NOTEMPTY;
01869         }
01870     }
01871 
01872     // remove the entry
01873     err = lfs_dir_remove(lfs, &cwd, &entry);
01874     if (err) {
01875         return err;
01876     }
01877 
01878     // if we were a directory, find pred, replace tail
01879     if (entry.d.type == LFS_TYPE_DIR) {
01880         int res = lfs_pred(lfs, dir.pair, &cwd);
01881         if (res < 0) {
01882             return res;
01883         }
01884 
01885         LFS_ASSERT(res); // must have pred
01886         cwd.d.tail[0] = dir.d.tail[0];
01887         cwd.d.tail[1] = dir.d.tail[1];
01888 
01889         err = lfs_dir_commit(lfs, &cwd, NULL, 0);
01890         if (err) {
01891             return err;
01892         }
01893     }
01894 
01895     return 0;
01896 }
01897 
01898 int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
01899     // deorphan if we haven't yet, needed at most once after poweron
01900     if (!lfs->deorphaned) {
01901         int err = lfs_deorphan(lfs);
01902         if (err) {
01903             return err;
01904         }
01905     }
01906 
01907     // find old entry
01908     lfs_dir_t oldcwd;
01909     int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root);
01910     if (err) {
01911         return err;
01912     }
01913 
01914     lfs_entry_t oldentry;
01915     err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath);
01916     if (err) {
01917         return err;
01918     }
01919 
01920     // allocate new entry
01921     lfs_dir_t newcwd;
01922     err = lfs_dir_fetch(lfs, &newcwd, lfs->root);
01923     if (err) {
01924         return err;
01925     }
01926 
01927     lfs_entry_t preventry;
01928     err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath);
01929     if (err && (err != LFS_ERR_NOENT || strchr(newpath, '/') != NULL)) {
01930         return err;
01931     }
01932 
01933     bool prevexists = (err != LFS_ERR_NOENT);
01934     bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0);
01935 
01936     // must have same type
01937     if (prevexists && preventry.d.type != oldentry.d.type) {
01938         return LFS_ERR_ISDIR;
01939     }
01940 
01941     lfs_dir_t dir;
01942     if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
01943         // must be empty before removal, checking size
01944         // without masking top bit checks for any case where
01945         // dir is not empty
01946         err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir);
01947         if (err) {
01948             return err;
01949         } else if (dir.d.size != sizeof(dir.d)+4) {
01950             return LFS_ERR_NOTEMPTY;
01951         }
01952     }
01953 
01954     // mark as moving
01955     oldentry.d.type |= 0x80;
01956     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL);
01957     if (err) {
01958         return err;
01959     }
01960 
01961     // update pair if newcwd == oldcwd
01962     if (samepair) {
01963         newcwd = oldcwd;
01964     }
01965 
01966     // move to new location
01967     lfs_entry_t newentry = preventry;
01968     newentry.d = oldentry.d;
01969     newentry.d.type &= ~0x80;
01970     newentry.d.nlen = strlen(newpath);
01971 
01972     if (prevexists) {
01973         err = lfs_dir_update(lfs, &newcwd, &newentry, newpath);
01974         if (err) {
01975             return err;
01976         }
01977     } else {
01978         err = lfs_dir_append(lfs, &newcwd, &newentry, newpath);
01979         if (err) {
01980             return err;
01981         }
01982     }
01983 
01984     // update pair if newcwd == oldcwd
01985     if (samepair) {
01986         oldcwd = newcwd;
01987     }
01988 
01989     // remove old entry
01990     err = lfs_dir_remove(lfs, &oldcwd, &oldentry);
01991     if (err) {
01992         return err;
01993     }
01994 
01995     // if we were a directory, find pred, replace tail
01996     if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
01997         int res = lfs_pred(lfs, dir.pair, &newcwd);
01998         if (res < 0) {
01999             return res;
02000         }
02001 
02002         LFS_ASSERT(res); // must have pred
02003         newcwd.d.tail[0] = dir.d.tail[0];
02004         newcwd.d.tail[1] = dir.d.tail[1];
02005 
02006         err = lfs_dir_commit(lfs, &newcwd, NULL, 0);
02007         if (err) {
02008             return err;
02009         }
02010     }
02011 
02012     return 0;
02013 }
02014 
02015 
02016 /// Filesystem operations ///
02017 static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
02018     lfs->cfg = cfg;
02019 
02020     // setup read cache
02021     lfs->rcache.block = 0xffffffff;
02022     if (lfs->cfg->read_buffer) {
02023         lfs->rcache.buffer = lfs->cfg->read_buffer;
02024     } else {
02025         lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size);
02026         if (!lfs->rcache.buffer) {
02027             return LFS_ERR_NOMEM;
02028         }
02029     }
02030 
02031     // setup program cache
02032     lfs->pcache.block = 0xffffffff;
02033     if (lfs->cfg->prog_buffer) {
02034         lfs->pcache.buffer = lfs->cfg->prog_buffer;
02035     } else {
02036         lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size);
02037         if (!lfs->pcache.buffer) {
02038             return LFS_ERR_NOMEM;
02039         }
02040     }
02041 
02042     // setup lookahead, round down to nearest 32-bits
02043     LFS_ASSERT(lfs->cfg->lookahead % 32 == 0);
02044     LFS_ASSERT(lfs->cfg->lookahead > 0);
02045     if (lfs->cfg->lookahead_buffer) {
02046         lfs->free.buffer = lfs->cfg->lookahead_buffer;
02047     } else {
02048         lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead/8);
02049         if (!lfs->free.buffer) {
02050             return LFS_ERR_NOMEM;
02051         }
02052     }
02053 
02054     // check that program and read sizes are multiples of the block size
02055     LFS_ASSERT(lfs->cfg->prog_size % lfs->cfg->read_size == 0);
02056     LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->prog_size == 0);
02057 
02058     // check that the block size is large enough to fit ctz pointers
02059     LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
02060             <= lfs->cfg->block_size);
02061 
02062     // setup default state
02063     lfs->root[0] = 0xffffffff;
02064     lfs->root[1] = 0xffffffff;
02065     lfs->files = NULL;
02066     lfs->dirs = NULL;
02067     lfs->deorphaned = false;
02068 
02069     return 0;
02070 }
02071 
02072 static int lfs_deinit(lfs_t *lfs) {
02073     // free allocated memory
02074     if (!lfs->cfg->read_buffer) {
02075         lfs_free(lfs->rcache.buffer);
02076     }
02077 
02078     if (!lfs->cfg->prog_buffer) {
02079         lfs_free(lfs->pcache.buffer);
02080     }
02081 
02082     if (!lfs->cfg->lookahead_buffer) {
02083         lfs_free(lfs->free.buffer);
02084     }
02085 
02086     return 0;
02087 }
02088 
02089 int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
02090     int err = lfs_init(lfs, cfg);
02091     if (err) {
02092         return err;
02093     }
02094 
02095     // create free lookahead
02096     memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8);
02097     lfs->free.begin = 0;
02098     lfs->free.size = lfs_min(lfs->cfg->lookahead, lfs->cfg->block_count);
02099     lfs->free.off = 0;
02100     lfs_alloc_ack(lfs);
02101 
02102     // create superblock dir
02103     lfs_dir_t superdir;
02104     err = lfs_dir_alloc(lfs, &superdir);
02105     if (err) {
02106         return err;
02107     }
02108 
02109     // write root directory
02110     lfs_dir_t root;
02111     err = lfs_dir_alloc(lfs, &root);
02112     if (err) {
02113         return err;
02114     }
02115 
02116     err = lfs_dir_commit(lfs, &root, NULL, 0);
02117     if (err) {
02118         return err;
02119     }
02120 
02121     lfs->root[0] = root.pair[0];
02122     lfs->root[1] = root.pair[1];
02123 
02124     // write superblocks
02125     lfs_superblock_t superblock = {
02126         .off = sizeof(superdir.d),
02127         .d.type = LFS_TYPE_SUPERBLOCK,
02128         .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4,
02129         .d.nlen = sizeof(superblock.d.magic),
02130         .d.version = LFS_DISK_VERSION,
02131         .d.magic = {"littlefs"},
02132         .d.block_size  = lfs->cfg->block_size,
02133         .d.block_count = lfs->cfg->block_count,
02134         .d.root = {lfs->root[0], lfs->root[1]},
02135     };
02136     superdir.d.tail[0] = root.pair[0];
02137     superdir.d.tail[1] = root.pair[1];
02138     superdir.d.size = sizeof(superdir.d) + sizeof(superblock.d) + 4;
02139 
02140     // write both pairs to be safe
02141     lfs_superblock_tole32(&superblock.d);
02142     bool valid = false;
02143     for (int i = 0; i < 2; i++) {
02144         err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){
02145                 {sizeof(superdir.d), sizeof(superblock.d),
02146                  &superblock.d, sizeof(superblock.d)}
02147             }, 1);
02148         if (err && err != LFS_ERR_CORRUPT) {
02149             return err;
02150         }
02151 
02152         valid = valid || !err;
02153     }
02154 
02155     if (!valid) {
02156         return LFS_ERR_CORRUPT;
02157     }
02158 
02159     // sanity check that fetch works
02160     err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1});
02161     if (err) {
02162         return err;
02163     }
02164 
02165     lfs_alloc_ack(lfs);
02166     return lfs_deinit(lfs);
02167 }
02168 
02169 int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
02170     int err = lfs_init(lfs, cfg);
02171     if (err) {
02172         return err;
02173     }
02174 
02175     // setup free lookahead
02176     lfs->free.begin = 0;
02177     lfs->free.size = 0;
02178     lfs->free.off = 0;
02179     lfs_alloc_ack(lfs);
02180 
02181     // load superblock
02182     lfs_dir_t dir;
02183     lfs_superblock_t superblock;
02184     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
02185     if (err && err != LFS_ERR_CORRUPT) {
02186         return err;
02187     }
02188 
02189     if (!err) {
02190         err = lfs_bd_read(lfs, dir.pair[0], sizeof(dir.d),
02191                 &superblock.d, sizeof(superblock.d));
02192         lfs_superblock_fromle32(&superblock.d);
02193         if (err) {
02194             return err;
02195         }
02196 
02197         lfs->root[0] = superblock.d.root[0];
02198         lfs->root[1] = superblock.d.root[1];
02199     }
02200 
02201     if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
02202         LFS_ERROR("Invalid superblock at %ld %ld", dir.pair[0], dir.pair[1]);
02203         return LFS_ERR_CORRUPT;
02204     }
02205 
02206     uint16_t major_version = (0xffff & (superblock.d.version >> 16));
02207     uint16_t minor_version = (0xffff & (superblock.d.version >>  0));
02208     if ((major_version != LFS_DISK_VERSION_MAJOR ||
02209          minor_version > LFS_DISK_VERSION_MINOR)) {
02210         LFS_ERROR("Invalid version %ld.%ld", major_version, minor_version);
02211         return LFS_ERR_INVAL;
02212     }
02213 
02214     return 0;
02215 }
02216 
02217 int lfs_unmount(lfs_t *lfs) {
02218     return lfs_deinit(lfs);
02219 }
02220 
02221 
02222 /// Littlefs specific operations ///
02223 int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
02224     if (lfs_pairisnull(lfs->root)) {
02225         return 0;
02226     }
02227 
02228     // iterate over metadata pairs
02229     lfs_dir_t dir;
02230     lfs_entry_t entry;
02231     lfs_block_t cwd[2] = {0, 1};
02232 
02233     while (true) {
02234         for (int i = 0; i < 2; i++) {
02235             int err = cb(data, cwd[i]);
02236             if (err) {
02237                 return err;
02238             }
02239         }
02240 
02241         int err = lfs_dir_fetch(lfs, &dir, cwd);
02242         if (err) {
02243             return err;
02244         }
02245 
02246         // iterate over contents
02247         while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) {
02248             err = lfs_bd_read(lfs, dir.pair[0], dir.off,
02249                     &entry.d, sizeof(entry.d));
02250             lfs_entry_fromle32(&entry.d);
02251             if (err) {
02252                 return err;
02253             }
02254 
02255             dir.off += lfs_entry_size(&entry);
02256             if ((0x70 & entry.d.type) == (0x70 & LFS_TYPE_REG)) {
02257                 err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL,
02258                         entry.d.u.file.head, entry.d.u.file.size, cb, data);
02259                 if (err) {
02260                     return err;
02261                 }
02262             }
02263         }
02264 
02265         cwd[0] = dir.d.tail[0];
02266         cwd[1] = dir.d.tail[1];
02267 
02268         if (lfs_pairisnull(cwd)) {
02269             break;
02270         }
02271     }
02272 
02273     // iterate over any open files
02274     for (lfs_file_t *f = lfs->files; f; f = f->next) {
02275         if (f->flags & LFS_F_DIRTY) {
02276             int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache,
02277                     f->head, f->size, cb, data);
02278             if (err) {
02279                 return err;
02280             }
02281         }
02282 
02283         if (f->flags & LFS_F_WRITING) {
02284             int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache,
02285                     f->block, f->pos, cb, data);
02286             if (err) {
02287                 return err;
02288             }
02289         }
02290     }
02291 
02292     return 0;
02293 }
02294 
02295 static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir) {
02296     if (lfs_pairisnull(lfs->root)) {
02297         return 0;
02298     }
02299 
02300     // iterate over all directory directory entries
02301     int err = lfs_dir_fetch(lfs, pdir, (const lfs_block_t[2]){0, 1});
02302     if (err) {
02303         return err;
02304     }
02305 
02306     while (!lfs_pairisnull(pdir->d.tail)) {
02307         if (lfs_paircmp(pdir->d.tail, dir) == 0) {
02308             return true;
02309         }
02310 
02311         err = lfs_dir_fetch(lfs, pdir, pdir->d.tail);
02312         if (err) {
02313             return err;
02314         }
02315     }
02316 
02317     return false;
02318 }
02319 
02320 static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
02321         lfs_dir_t *parent, lfs_entry_t *entry) {
02322     if (lfs_pairisnull(lfs->root)) {
02323         return 0;
02324     }
02325 
02326     parent->d.tail[0] = 0;
02327     parent->d.tail[1] = 1;
02328 
02329     // iterate over all directory directory entries
02330     while (!lfs_pairisnull(parent->d.tail)) {
02331         int err = lfs_dir_fetch(lfs, parent, parent->d.tail);
02332         if (err) {
02333             return err;
02334         }
02335 
02336         while (true) {
02337             err = lfs_dir_next(lfs, parent, entry);
02338             if (err && err != LFS_ERR_NOENT) {
02339                 return err;
02340             }
02341 
02342             if (err == LFS_ERR_NOENT) {
02343                 break;
02344             }
02345 
02346             if (((0x70 & entry->d.type) == (0x70 & LFS_TYPE_DIR)) &&
02347                  lfs_paircmp(entry->d.u.dir, dir) == 0) {
02348                 return true;
02349             }
02350         }
02351     }
02352 
02353     return false;
02354 }
02355 
02356 static int lfs_moved(lfs_t *lfs, const void *e) {
02357     if (lfs_pairisnull(lfs->root)) {
02358         return 0;
02359     }
02360 
02361     // skip superblock
02362     lfs_dir_t cwd;
02363     int err = lfs_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1});
02364     if (err) {
02365         return err;
02366     }
02367 
02368     // iterate over all directory directory entries
02369     lfs_entry_t entry;
02370     while (!lfs_pairisnull(cwd.d.tail)) {
02371         err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail);
02372         if (err) {
02373             return err;
02374         }
02375 
02376         while (true) {
02377             err = lfs_dir_next(lfs, &cwd, &entry);
02378             if (err && err != LFS_ERR_NOENT) {
02379                 return err;
02380             }
02381 
02382             if (err == LFS_ERR_NOENT) {
02383                 break;
02384             }
02385 
02386             if (!(0x80 & entry.d.type) &&
02387                  memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) {
02388                 return true;
02389             }
02390         }
02391     }
02392 
02393     return false;
02394 }
02395 
02396 static int lfs_relocate(lfs_t *lfs,
02397         const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) {
02398     // find parent
02399     lfs_dir_t parent;
02400     lfs_entry_t entry;
02401     int res = lfs_parent(lfs, oldpair, &parent, &entry);
02402     if (res < 0) {
02403         return res;
02404     }
02405 
02406     if (res) {
02407         // update disk, this creates a desync
02408         entry.d.u.dir[0] = newpair[0];
02409         entry.d.u.dir[1] = newpair[1];
02410 
02411         int err = lfs_dir_update(lfs, &parent, &entry, NULL);
02412         if (err) {
02413             return err;
02414         }
02415 
02416         // update internal root
02417         if (lfs_paircmp(oldpair, lfs->root) == 0) {
02418             LFS_DEBUG("Relocating root %ld %ld", newpair[0], newpair[1]);
02419             lfs->root[0] = newpair[0];
02420             lfs->root[1] = newpair[1];
02421         }
02422 
02423         // clean up bad block, which should now be a desync
02424         return lfs_deorphan(lfs);
02425     }
02426 
02427     // find pred
02428     res = lfs_pred(lfs, oldpair, &parent);
02429     if (res < 0) {
02430         return res;
02431     }
02432 
02433     if (res) {
02434         // just replace bad pair, no desync can occur
02435         parent.d.tail[0] = newpair[0];
02436         parent.d.tail[1] = newpair[1];
02437 
02438         return lfs_dir_commit(lfs, &parent, NULL, 0);
02439     }
02440 
02441     // couldn't find dir, must be new
02442     return 0;
02443 }
02444 
02445 int lfs_deorphan(lfs_t *lfs) {
02446     lfs->deorphaned = true;
02447 
02448     if (lfs_pairisnull(lfs->root)) {
02449         return 0;
02450     }
02451 
02452     lfs_dir_t pdir = {.d.size = 0x80000000};
02453     lfs_dir_t cwd = {.d.tail[0] = 0, .d.tail[1] = 1};
02454 
02455     // iterate over all directory directory entries
02456     while (!lfs_pairisnull(cwd.d.tail)) {
02457         int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail);
02458         if (err) {
02459             return err;
02460         }
02461 
02462         // check head blocks for orphans
02463         if (!(0x80000000 & pdir.d.size)) {
02464             // check if we have a parent
02465             lfs_dir_t parent;
02466             lfs_entry_t entry;
02467             int res = lfs_parent(lfs, pdir.d.tail, &parent, &entry);
02468             if (res < 0) {
02469                 return res;
02470             }
02471 
02472             if (!res) {
02473                 // we are an orphan
02474                 LFS_DEBUG("Found orphan %ld %ld",
02475                         pdir.d.tail[0], pdir.d.tail[1]);
02476 
02477                 pdir.d.tail[0] = cwd.d.tail[0];
02478                 pdir.d.tail[1] = cwd.d.tail[1];
02479 
02480                 err = lfs_dir_commit(lfs, &pdir, NULL, 0);
02481                 if (err) {
02482                     return err;
02483                 }
02484 
02485                 break;
02486             }
02487 
02488             if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail)) {
02489                 // we have desynced
02490                 LFS_DEBUG("Found desync %ld %ld",
02491                         entry.d.u.dir[0], entry.d.u.dir[1]);
02492 
02493                 pdir.d.tail[0] = entry.d.u.dir[0];
02494                 pdir.d.tail[1] = entry.d.u.dir[1];
02495 
02496                 err = lfs_dir_commit(lfs, &pdir, NULL, 0);
02497                 if (err) {
02498                     return err;
02499                 }
02500 
02501                 break;
02502             }
02503         }
02504 
02505         // check entries for moves
02506         lfs_entry_t entry;
02507         while (true) {
02508             err = lfs_dir_next(lfs, &cwd, &entry);
02509             if (err && err != LFS_ERR_NOENT) {
02510                 return err;
02511             }
02512 
02513             if (err == LFS_ERR_NOENT) {
02514                 break;
02515             }
02516 
02517             // found moved entry
02518             if (entry.d.type & 0x80) {
02519                 int moved = lfs_moved(lfs, &entry.d.u);
02520                 if (moved < 0) {
02521                     return moved;
02522                 }
02523 
02524                 if (moved) {
02525                     LFS_DEBUG("Found move %ld %ld",
02526                             entry.d.u.dir[0], entry.d.u.dir[1]);
02527                     err = lfs_dir_remove(lfs, &cwd, &entry);
02528                     if (err) {
02529                         return err;
02530                     }
02531                 } else {
02532                     LFS_DEBUG("Found partial move %ld %ld",
02533                             entry.d.u.dir[0], entry.d.u.dir[1]);
02534                     entry.d.type &= ~0x80;
02535                     err = lfs_dir_update(lfs, &cwd, &entry, NULL);
02536                     if (err) {
02537                         return err;
02538                     }
02539                 }
02540             }
02541         }
02542 
02543         memcpy(&pdir, &cwd, sizeof(pdir));
02544     }
02545 
02546     return 0;
02547 }
02548