Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
Generated on Tue Jul 12 2022 14:23:51 by
