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