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.
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
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 file->head = 0xffffffff; 01265 file->size = 0; 01266 } 01267 01268 // allocate buffer if needed 01269 file->cache.block = 0xffffffff; 01270 if (lfs->cfg->file_buffer) { 01271 file->cache.buffer = lfs->cfg->file_buffer; 01272 } else if ((file->flags & 3) == LFS_O_RDONLY) { 01273 file->cache.buffer = malloc(lfs->cfg->read_size); 01274 if (!file->cache.buffer) { 01275 return LFS_ERR_NOMEM; 01276 } 01277 } else { 01278 file->cache.buffer = malloc(lfs->cfg->prog_size); 01279 if (!file->cache.buffer) { 01280 return LFS_ERR_NOMEM; 01281 } 01282 } 01283 01284 // add to list of files 01285 file->next = lfs->files; 01286 lfs->files = file; 01287 01288 return 0; 01289 } 01290 01291 int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { 01292 int err = lfs_file_sync(lfs, file); 01293 01294 // remove from list of files 01295 for (lfs_file_t **p = &lfs->files; *p; p = &(*p)->next) { 01296 if (*p == file) { 01297 *p = file->next; 01298 break; 01299 } 01300 } 01301 01302 // clean up memory 01303 if (!lfs->cfg->file_buffer) { 01304 free(file->cache.buffer); 01305 } 01306 01307 return err; 01308 } 01309 01310 static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { 01311 relocate: 01312 LFS_DEBUG("Bad block at %ld", file->block); 01313 01314 // just relocate what exists into new block 01315 lfs_block_t nblock; 01316 int err = lfs_alloc(lfs, &nblock); 01317 if (err) { 01318 return err; 01319 } 01320 01321 err = lfs_bd_erase(lfs, nblock); 01322 if (err) { 01323 if (err == LFS_ERR_CORRUPT) { 01324 goto relocate; 01325 } 01326 return err; 01327 } 01328 01329 // either read from dirty cache or disk 01330 for (lfs_off_t i = 0; i < file->off; i++) { 01331 uint8_t data; 01332 err = lfs_cache_read(lfs, &lfs->rcache, &file->cache, 01333 file->block, i, &data, 1); 01334 if (err) { 01335 return err; 01336 } 01337 01338 err = lfs_cache_prog(lfs, &lfs->pcache, &lfs->rcache, 01339 nblock, i, &data, 1); 01340 if (err) { 01341 if (err == LFS_ERR_CORRUPT) { 01342 goto relocate; 01343 } 01344 return err; 01345 } 01346 } 01347 01348 // copy over new state of file 01349 memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->prog_size); 01350 file->cache.block = lfs->pcache.block; 01351 file->cache.off = lfs->pcache.off; 01352 lfs->pcache.block = 0xffffffff; 01353 01354 file->block = nblock; 01355 return 0; 01356 } 01357 01358 static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { 01359 if (file->flags & LFS_F_READING) { 01360 // just drop read cache 01361 file->cache.block = 0xffffffff; 01362 file->flags &= ~LFS_F_READING; 01363 } 01364 01365 if (file->flags & LFS_F_WRITING) { 01366 lfs_off_t pos = file->pos; 01367 01368 // copy over anything after current branch 01369 lfs_file_t orig = { 01370 .head = file->head, 01371 .size = file->size, 01372 .flags = LFS_O_RDONLY, 01373 .pos = file->pos, 01374 .cache = lfs->rcache, 01375 }; 01376 lfs->rcache.block = 0xffffffff; 01377 01378 while (file->pos < file->size) { 01379 // copy over a byte at a time, leave it up to caching 01380 // to make this efficient 01381 uint8_t data; 01382 lfs_ssize_t res = lfs_file_read(lfs, &orig, &data, 1); 01383 if (res < 0) { 01384 return res; 01385 } 01386 01387 res = lfs_file_write(lfs, file, &data, 1); 01388 if (res < 0) { 01389 return res; 01390 } 01391 01392 // keep our reference to the rcache in sync 01393 if (lfs->rcache.block != 0xffffffff) { 01394 orig.cache.block = 0xffffffff; 01395 lfs->rcache.block = 0xffffffff; 01396 } 01397 } 01398 01399 // write out what we have 01400 while (true) { 01401 int err = lfs_cache_flush(lfs, &file->cache, &lfs->rcache); 01402 if (err) { 01403 if (err == LFS_ERR_CORRUPT) { 01404 goto relocate; 01405 } 01406 return err; 01407 } 01408 01409 break; 01410 relocate: 01411 err = lfs_file_relocate(lfs, file); 01412 if (err) { 01413 return err; 01414 } 01415 } 01416 01417 // actual file updates 01418 file->head = file->block; 01419 file->size = file->pos; 01420 file->flags &= ~LFS_F_WRITING; 01421 file->flags |= LFS_F_DIRTY; 01422 01423 file->pos = pos; 01424 } 01425 01426 return 0; 01427 } 01428 01429 int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { 01430 int err = lfs_file_flush(lfs, file); 01431 if (err) { 01432 return err; 01433 } 01434 01435 if ((file->flags & LFS_F_DIRTY) && 01436 !(file->flags & LFS_F_ERRED) && 01437 !lfs_pairisnull(file->pair)) { 01438 // update dir entry 01439 lfs_dir_t cwd; 01440 int err = lfs_dir_fetch(lfs, &cwd, file->pair); 01441 if (err) { 01442 return err; 01443 } 01444 01445 lfs_entry_t entry = {.off = file->poff}; 01446 err = lfs_bd_read(lfs, cwd.pair[0], entry.off, 01447 &entry.d, sizeof(entry.d)); 01448 if (err) { 01449 return err; 01450 } 01451 01452 if (entry.d.type != LFS_TYPE_REG) { 01453 // sanity check valid entry 01454 return LFS_ERR_INVAL; 01455 } 01456 01457 entry.d.u.file.head = file->head; 01458 entry.d.u.file.size = file->size; 01459 01460 err = lfs_dir_update(lfs, &cwd, &entry, NULL); 01461 if (err) { 01462 return err; 01463 } 01464 01465 file->flags &= ~LFS_F_DIRTY; 01466 } 01467 01468 return 0; 01469 } 01470 01471 lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, 01472 void *buffer, lfs_size_t size) { 01473 uint8_t *data = buffer; 01474 lfs_size_t nsize = size; 01475 01476 if ((file->flags & 3) == LFS_O_WRONLY) { 01477 return LFS_ERR_INVAL; 01478 } 01479 01480 if (file->flags & LFS_F_WRITING) { 01481 // flush out any writes 01482 int err = lfs_file_flush(lfs, file); 01483 if (err) { 01484 return err; 01485 } 01486 } 01487 01488 if (file->pos >= file->size) { 01489 // eof if past end 01490 return 0; 01491 } 01492 01493 size = lfs_min(size, file->size - file->pos); 01494 nsize = size; 01495 01496 while (nsize > 0) { 01497 // check if we need a new block 01498 if (!(file->flags & LFS_F_READING) || 01499 file->off == lfs->cfg->block_size) { 01500 int err = lfs_ctz_find(lfs, &file->cache, NULL, 01501 file->head, file->size, 01502 file->pos, &file->block, &file->off); 01503 if (err) { 01504 return err; 01505 } 01506 01507 file->flags |= LFS_F_READING; 01508 } 01509 01510 // read as much as we can in current block 01511 lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); 01512 int err = lfs_cache_read(lfs, &file->cache, NULL, 01513 file->block, file->off, data, diff); 01514 if (err) { 01515 return err; 01516 } 01517 01518 file->pos += diff; 01519 file->off += diff; 01520 data += diff; 01521 nsize -= diff; 01522 } 01523 01524 return size; 01525 } 01526 01527 lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, 01528 const void *buffer, lfs_size_t size) { 01529 const uint8_t *data = buffer; 01530 lfs_size_t nsize = size; 01531 01532 if ((file->flags & 3) == LFS_O_RDONLY) { 01533 return LFS_ERR_INVAL; 01534 } 01535 01536 if (file->flags & LFS_F_READING) { 01537 // drop any reads 01538 int err = lfs_file_flush(lfs, file); 01539 if (err) { 01540 return err; 01541 } 01542 } 01543 01544 if ((file->flags & LFS_O_APPEND) && file->pos < file->size) { 01545 file->pos = file->size; 01546 } 01547 01548 if (!(file->flags & LFS_F_WRITING) && file->pos > file->size) { 01549 // fill with zeros 01550 lfs_off_t pos = file->pos; 01551 file->pos = file->size; 01552 01553 while (file->pos < pos) { 01554 lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); 01555 if (res < 0) { 01556 return res; 01557 } 01558 } 01559 } 01560 01561 while (nsize > 0) { 01562 // check if we need a new block 01563 if (!(file->flags & LFS_F_WRITING) || 01564 file->off == lfs->cfg->block_size) { 01565 if (!(file->flags & LFS_F_WRITING) && file->pos > 0) { 01566 // find out which block we're extending from 01567 int err = lfs_ctz_find(lfs, &file->cache, NULL, 01568 file->head, file->size, 01569 file->pos-1, &file->block, &file->off); 01570 if (err) { 01571 file->flags |= LFS_F_ERRED; 01572 return err; 01573 } 01574 01575 // mark cache as dirty since we may have read data into it 01576 file->cache.block = 0xffffffff; 01577 } 01578 01579 // extend file with new blocks 01580 lfs_alloc_ack(lfs); 01581 int err = lfs_ctz_extend(lfs, &lfs->rcache, &file->cache, 01582 file->block, file->pos, 01583 &file->block, &file->off); 01584 if (err) { 01585 file->flags |= LFS_F_ERRED; 01586 return err; 01587 } 01588 01589 file->flags |= LFS_F_WRITING; 01590 } 01591 01592 // program as much as we can in current block 01593 lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); 01594 while (true) { 01595 int err = lfs_cache_prog(lfs, &file->cache, &lfs->rcache, 01596 file->block, file->off, data, diff); 01597 if (err) { 01598 if (err == LFS_ERR_CORRUPT) { 01599 goto relocate; 01600 } 01601 file->flags |= LFS_F_ERRED; 01602 return err; 01603 } 01604 01605 break; 01606 relocate: 01607 err = lfs_file_relocate(lfs, file); 01608 if (err) { 01609 file->flags |= LFS_F_ERRED; 01610 return err; 01611 } 01612 } 01613 01614 file->pos += diff; 01615 file->off += diff; 01616 data += diff; 01617 nsize -= diff; 01618 01619 lfs_alloc_ack(lfs); 01620 } 01621 01622 file->flags &= ~LFS_F_ERRED; 01623 return size; 01624 } 01625 01626 lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, 01627 lfs_soff_t off, int whence) { 01628 // write out everything beforehand, may be noop if rdonly 01629 int err = lfs_file_flush(lfs, file); 01630 if (err) { 01631 return err; 01632 } 01633 01634 // update pos 01635 if (whence == LFS_SEEK_SET) { 01636 file->pos = off; 01637 } else if (whence == LFS_SEEK_CUR) { 01638 if ((lfs_off_t)-off > file->pos) { 01639 return LFS_ERR_INVAL; 01640 } 01641 01642 file->pos = file->pos + off; 01643 } else if (whence == LFS_SEEK_END) { 01644 if ((lfs_off_t)-off > file->size) { 01645 return LFS_ERR_INVAL; 01646 } 01647 01648 file->pos = file->size + off; 01649 } 01650 01651 return file->pos; 01652 } 01653 01654 lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { 01655 return file->pos; 01656 } 01657 01658 int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { 01659 lfs_soff_t res = lfs_file_seek(lfs, file, 0, LFS_SEEK_SET); 01660 if (res < 0) { 01661 return res; 01662 } 01663 01664 return 0; 01665 } 01666 01667 lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { 01668 return lfs_max(file->pos, file->size); 01669 } 01670 01671 01672 /// General fs oprations /// 01673 int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { 01674 // check for root, can only be something like '/././../.' 01675 if (strspn(path, "/.") == strlen(path)) { 01676 memset(info, 0, sizeof(*info)); 01677 info->type = LFS_TYPE_DIR; 01678 strcpy(info->name, "/"); 01679 return 0; 01680 } 01681 01682 lfs_dir_t cwd; 01683 int err = lfs_dir_fetch(lfs, &cwd, lfs->root); 01684 if (err) { 01685 return err; 01686 } 01687 01688 lfs_entry_t entry; 01689 err = lfs_dir_find(lfs, &cwd, &entry, &path); 01690 if (err) { 01691 return err; 01692 } 01693 01694 memset(info, 0, sizeof(*info)); 01695 info->type = entry.d.type; 01696 if (info->type == LFS_TYPE_REG) { 01697 info->size = entry.d.u.file.size; 01698 } 01699 01700 err = lfs_bd_read(lfs, cwd.pair[0], 01701 entry.off + 4+entry.d.elen+entry.d.alen, 01702 info->name, entry.d.nlen); 01703 if (err) { 01704 return err; 01705 } 01706 01707 return 0; 01708 } 01709 01710 int lfs_remove(lfs_t *lfs, const char *path) { 01711 // deorphan if we haven't yet, needed at most once after poweron 01712 if (!lfs->deorphaned) { 01713 int err = lfs_deorphan(lfs); 01714 if (err) { 01715 return err; 01716 } 01717 } 01718 01719 lfs_dir_t cwd; 01720 int err = lfs_dir_fetch(lfs, &cwd, lfs->root); 01721 if (err) { 01722 return err; 01723 } 01724 01725 lfs_entry_t entry; 01726 err = lfs_dir_find(lfs, &cwd, &entry, &path); 01727 if (err) { 01728 return err; 01729 } 01730 01731 lfs_dir_t dir; 01732 if (entry.d.type == LFS_TYPE_DIR) { 01733 // must be empty before removal, checking size 01734 // without masking top bit checks for any case where 01735 // dir is not empty 01736 int err = lfs_dir_fetch(lfs, &dir, entry.d.u.dir); 01737 if (err) { 01738 return err; 01739 } else if (dir.d.size != sizeof(dir.d)+4) { 01740 return LFS_ERR_INVAL; 01741 } 01742 } 01743 01744 // remove the entry 01745 err = lfs_dir_remove(lfs, &cwd, &entry); 01746 if (err) { 01747 return err; 01748 } 01749 01750 // if we were a directory, find pred, replace tail 01751 if (entry.d.type == LFS_TYPE_DIR) { 01752 int res = lfs_pred(lfs, dir.pair, &cwd); 01753 if (res < 0) { 01754 return res; 01755 } 01756 01757 assert(res); // must have pred 01758 cwd.d.tail[0] = dir.d.tail[0]; 01759 cwd.d.tail[1] = dir.d.tail[1]; 01760 01761 int err = lfs_dir_commit(lfs, &cwd, NULL, 0); 01762 if (err) { 01763 return err; 01764 } 01765 } 01766 01767 return 0; 01768 } 01769 01770 int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { 01771 // deorphan if we haven't yet, needed at most once after poweron 01772 if (!lfs->deorphaned) { 01773 int err = lfs_deorphan(lfs); 01774 if (err) { 01775 return err; 01776 } 01777 } 01778 01779 // find old entry 01780 lfs_dir_t oldcwd; 01781 int err = lfs_dir_fetch(lfs, &oldcwd, lfs->root); 01782 if (err) { 01783 return err; 01784 } 01785 01786 lfs_entry_t oldentry; 01787 err = lfs_dir_find(lfs, &oldcwd, &oldentry, &oldpath); 01788 if (err) { 01789 return err; 01790 } 01791 01792 // allocate new entry 01793 lfs_dir_t newcwd; 01794 err = lfs_dir_fetch(lfs, &newcwd, lfs->root); 01795 if (err) { 01796 return err; 01797 } 01798 01799 lfs_entry_t preventry; 01800 err = lfs_dir_find(lfs, &newcwd, &preventry, &newpath); 01801 if (err && (err != LFS_ERR_NOENT || strchr(newpath, '/') != NULL)) { 01802 return err; 01803 } 01804 01805 bool prevexists = (err != LFS_ERR_NOENT); 01806 bool samepair = (lfs_paircmp(oldcwd.pair, newcwd.pair) == 0); 01807 01808 // must have same type 01809 if (prevexists && preventry.d.type != oldentry.d.type) { 01810 return LFS_ERR_INVAL; 01811 } 01812 01813 lfs_dir_t dir; 01814 if (prevexists && preventry.d.type == LFS_TYPE_DIR) { 01815 // must be empty before removal, checking size 01816 // without masking top bit checks for any case where 01817 // dir is not empty 01818 int err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir); 01819 if (err) { 01820 return err; 01821 } else if (dir.d.size != sizeof(dir.d)+4) { 01822 return LFS_ERR_INVAL; 01823 } 01824 } 01825 01826 // mark as moving 01827 oldentry.d.type |= 0x80; 01828 err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); 01829 if (err) { 01830 return err; 01831 } 01832 01833 // update pair if newcwd == oldcwd 01834 if (samepair) { 01835 newcwd = oldcwd; 01836 } 01837 01838 // move to new location 01839 lfs_entry_t newentry = preventry; 01840 newentry.d = oldentry.d; 01841 newentry.d.type &= ~0x80; 01842 newentry.d.nlen = strlen(newpath); 01843 01844 if (prevexists) { 01845 int err = lfs_dir_update(lfs, &newcwd, &newentry, newpath); 01846 if (err) { 01847 return err; 01848 } 01849 } else { 01850 int err = lfs_dir_append(lfs, &newcwd, &newentry, newpath); 01851 if (err) { 01852 return err; 01853 } 01854 } 01855 01856 // update pair if newcwd == oldcwd 01857 if (samepair) { 01858 oldcwd = newcwd; 01859 } 01860 01861 // remove old entry 01862 err = lfs_dir_remove(lfs, &oldcwd, &oldentry); 01863 if (err) { 01864 return err; 01865 } 01866 01867 // if we were a directory, find pred, replace tail 01868 if (prevexists && preventry.d.type == LFS_TYPE_DIR) { 01869 int res = lfs_pred(lfs, dir.pair, &newcwd); 01870 if (res < 0) { 01871 return res; 01872 } 01873 01874 assert(res); // must have pred 01875 newcwd.d.tail[0] = dir.d.tail[0]; 01876 newcwd.d.tail[1] = dir.d.tail[1]; 01877 01878 int err = lfs_dir_commit(lfs, &newcwd, NULL, 0); 01879 if (err) { 01880 return err; 01881 } 01882 } 01883 01884 return 0; 01885 } 01886 01887 01888 /// Filesystem operations /// 01889 static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { 01890 lfs->cfg = cfg; 01891 01892 // setup read cache 01893 lfs->rcache.block = 0xffffffff; 01894 if (lfs->cfg->read_buffer) { 01895 lfs->rcache.buffer = lfs->cfg->read_buffer; 01896 } else { 01897 lfs->rcache.buffer = malloc(lfs->cfg->read_size); 01898 if (!lfs->rcache.buffer) { 01899 return LFS_ERR_NOMEM; 01900 } 01901 } 01902 01903 // setup program cache 01904 lfs->pcache.block = 0xffffffff; 01905 if (lfs->cfg->prog_buffer) { 01906 lfs->pcache.buffer = lfs->cfg->prog_buffer; 01907 } else { 01908 lfs->pcache.buffer = malloc(lfs->cfg->prog_size); 01909 if (!lfs->pcache.buffer) { 01910 return LFS_ERR_NOMEM; 01911 } 01912 } 01913 01914 // setup lookahead, round down to nearest 32-bits 01915 assert(lfs->cfg->lookahead % 32 == 0); 01916 assert(lfs->cfg->lookahead > 0); 01917 if (lfs->cfg->lookahead_buffer) { 01918 lfs->free.buffer = lfs->cfg->lookahead_buffer; 01919 } else { 01920 lfs->free.buffer = malloc(lfs->cfg->lookahead/8); 01921 if (!lfs->free.buffer) { 01922 return LFS_ERR_NOMEM; 01923 } 01924 } 01925 01926 // check that the block size is large enough to fit ctz pointers 01927 assert(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) 01928 <= lfs->cfg->block_size); 01929 01930 // setup default state 01931 lfs->root[0] = 0xffffffff; 01932 lfs->root[1] = 0xffffffff; 01933 lfs->files = NULL; 01934 lfs->dirs = NULL; 01935 lfs->deorphaned = false; 01936 01937 return 0; 01938 } 01939 01940 static int lfs_deinit(lfs_t *lfs) { 01941 // free allocated memory 01942 if (!lfs->cfg->read_buffer) { 01943 free(lfs->rcache.buffer); 01944 } 01945 01946 if (!lfs->cfg->prog_buffer) { 01947 free(lfs->pcache.buffer); 01948 } 01949 01950 if (!lfs->cfg->lookahead_buffer) { 01951 free(lfs->free.buffer); 01952 } 01953 01954 return 0; 01955 } 01956 01957 int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { 01958 int err = lfs_init(lfs, cfg); 01959 if (err) { 01960 return err; 01961 } 01962 01963 // create free lookahead 01964 memset(lfs->free.buffer, 0, lfs->cfg->lookahead/8); 01965 lfs->free.begin = 0; 01966 lfs->free.off = 0; 01967 lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count; 01968 01969 // create superblock dir 01970 lfs_alloc_ack(lfs); 01971 lfs_dir_t superdir; 01972 err = lfs_dir_alloc(lfs, &superdir); 01973 if (err) { 01974 return err; 01975 } 01976 01977 // write root directory 01978 lfs_dir_t root; 01979 err = lfs_dir_alloc(lfs, &root); 01980 if (err) { 01981 return err; 01982 } 01983 01984 err = lfs_dir_commit(lfs, &root, NULL, 0); 01985 if (err) { 01986 return err; 01987 } 01988 01989 lfs->root[0] = root.pair[0]; 01990 lfs->root[1] = root.pair[1]; 01991 01992 // write superblocks 01993 lfs_superblock_t superblock = { 01994 .off = sizeof(superdir.d), 01995 .d.type = LFS_TYPE_SUPERBLOCK, 01996 .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, 01997 .d.nlen = sizeof(superblock.d.magic), 01998 .d.version = 0x00010001, 01999 .d.magic = {"littlefs"}, 02000 .d.block_size = lfs->cfg->block_size, 02001 .d.block_count = lfs->cfg->block_count, 02002 .d.root = {lfs->root[0], lfs->root[1]}, 02003 }; 02004 superdir.d.tail[0] = root.pair[0]; 02005 superdir.d.tail[1] = root.pair[1]; 02006 superdir.d.size = sizeof(superdir.d) + sizeof(superblock.d) + 4; 02007 02008 // write both pairs to be safe 02009 bool valid = false; 02010 for (int i = 0; i < 2; i++) { 02011 int err = lfs_dir_commit(lfs, &superdir, (struct lfs_region[]){ 02012 {sizeof(superdir.d), sizeof(superblock.d), 02013 &superblock.d, sizeof(superblock.d)} 02014 }, 1); 02015 if (err && err != LFS_ERR_CORRUPT) { 02016 return err; 02017 } 02018 02019 valid = valid || !err; 02020 } 02021 02022 if (!valid) { 02023 return LFS_ERR_CORRUPT; 02024 } 02025 02026 // sanity check that fetch works 02027 err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); 02028 if (err) { 02029 return err; 02030 } 02031 02032 lfs_alloc_ack(lfs); 02033 return lfs_deinit(lfs); 02034 } 02035 02036 int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { 02037 int err = lfs_init(lfs, cfg); 02038 if (err) { 02039 return err; 02040 } 02041 02042 // setup free lookahead 02043 lfs->free.begin = -lfs->cfg->lookahead; 02044 lfs->free.off = lfs->cfg->lookahead; 02045 lfs->free.end = lfs->free.begin + lfs->free.off + lfs->cfg->block_count; 02046 02047 // load superblock 02048 lfs_dir_t dir; 02049 lfs_superblock_t superblock; 02050 err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); 02051 if (err && err != LFS_ERR_CORRUPT) { 02052 return err; 02053 } 02054 02055 if (!err) { 02056 int err = lfs_bd_read(lfs, dir.pair[0], sizeof(dir.d), 02057 &superblock.d, sizeof(superblock.d)); 02058 if (err) { 02059 return err; 02060 } 02061 02062 lfs->root[0] = superblock.d.root[0]; 02063 lfs->root[1] = superblock.d.root[1]; 02064 } 02065 02066 if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { 02067 LFS_ERROR("Invalid superblock at %ld %ld", dir.pair[0], dir.pair[1]); 02068 return LFS_ERR_CORRUPT; 02069 } 02070 02071 if (superblock.d.version > (0x00010001 | 0x0000ffff)) { 02072 LFS_ERROR("Invalid version %ld.%ld", 02073 0xffff & (superblock.d.version >> 16), 02074 0xffff & (superblock.d.version >> 0)); 02075 return LFS_ERR_INVAL; 02076 } 02077 02078 return 0; 02079 } 02080 02081 int lfs_unmount(lfs_t *lfs) { 02082 return lfs_deinit(lfs); 02083 } 02084 02085 02086 /// Littlefs specific operations /// 02087 int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { 02088 if (lfs_pairisnull(lfs->root)) { 02089 return 0; 02090 } 02091 02092 // iterate over metadata pairs 02093 lfs_dir_t dir; 02094 lfs_entry_t entry; 02095 lfs_block_t cwd[2] = {0, 1}; 02096 02097 while (true) { 02098 for (int i = 0; i < 2; i++) { 02099 int err = cb(data, cwd[i]); 02100 if (err) { 02101 return err; 02102 } 02103 } 02104 02105 int err = lfs_dir_fetch(lfs, &dir, cwd); 02106 if (err) { 02107 return err; 02108 } 02109 02110 // iterate over contents 02111 while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { 02112 int err = lfs_bd_read(lfs, dir.pair[0], dir.off, 02113 &entry.d, sizeof(entry.d)); 02114 if (err) { 02115 return err; 02116 } 02117 02118 dir.off += lfs_entry_size(&entry); 02119 if ((0x70 & entry.d.type) == (0x70 & LFS_TYPE_REG)) { 02120 int err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL, 02121 entry.d.u.file.head, entry.d.u.file.size, cb, data); 02122 if (err) { 02123 return err; 02124 } 02125 } 02126 } 02127 02128 cwd[0] = dir.d.tail[0]; 02129 cwd[1] = dir.d.tail[1]; 02130 02131 if (lfs_pairisnull(cwd)) { 02132 break; 02133 } 02134 } 02135 02136 // iterate over any open files 02137 for (lfs_file_t *f = lfs->files; f; f = f->next) { 02138 if (f->flags & LFS_F_DIRTY) { 02139 int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache, 02140 f->head, f->size, cb, data); 02141 if (err) { 02142 return err; 02143 } 02144 } 02145 02146 if (f->flags & LFS_F_WRITING) { 02147 int err = lfs_ctz_traverse(lfs, &lfs->rcache, &f->cache, 02148 f->block, f->pos, cb, data); 02149 if (err) { 02150 return err; 02151 } 02152 } 02153 } 02154 02155 return 0; 02156 } 02157 02158 static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_dir_t *pdir) { 02159 if (lfs_pairisnull(lfs->root)) { 02160 return 0; 02161 } 02162 02163 // iterate over all directory directory entries 02164 int err = lfs_dir_fetch(lfs, pdir, (const lfs_block_t[2]){0, 1}); 02165 if (err) { 02166 return err; 02167 } 02168 02169 while (!lfs_pairisnull(pdir->d.tail)) { 02170 if (lfs_paircmp(pdir->d.tail, dir) == 0) { 02171 return true; 02172 } 02173 02174 int err = lfs_dir_fetch(lfs, pdir, pdir->d.tail); 02175 if (err) { 02176 return err; 02177 } 02178 } 02179 02180 return false; 02181 } 02182 02183 static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2], 02184 lfs_dir_t *parent, lfs_entry_t *entry) { 02185 if (lfs_pairisnull(lfs->root)) { 02186 return 0; 02187 } 02188 02189 parent->d.tail[0] = 0; 02190 parent->d.tail[1] = 1; 02191 02192 // iterate over all directory directory entries 02193 while (!lfs_pairisnull(parent->d.tail)) { 02194 int err = lfs_dir_fetch(lfs, parent, parent->d.tail); 02195 if (err) { 02196 return err; 02197 } 02198 02199 while (true) { 02200 int err = lfs_dir_next(lfs, parent, entry); 02201 if (err && err != LFS_ERR_NOENT) { 02202 return err; 02203 } 02204 02205 if (err == LFS_ERR_NOENT) { 02206 break; 02207 } 02208 02209 if (((0x70 & entry->d.type) == (0x70 & LFS_TYPE_DIR)) && 02210 lfs_paircmp(entry->d.u.dir, dir) == 0) { 02211 return true; 02212 } 02213 } 02214 } 02215 02216 return false; 02217 } 02218 02219 static int lfs_moved(lfs_t *lfs, const void *e) { 02220 if (lfs_pairisnull(lfs->root)) { 02221 return 0; 02222 } 02223 02224 // skip superblock 02225 lfs_dir_t cwd; 02226 int err = lfs_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); 02227 if (err) { 02228 return err; 02229 } 02230 02231 // iterate over all directory directory entries 02232 lfs_entry_t entry; 02233 while (!lfs_pairisnull(cwd.d.tail)) { 02234 int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail); 02235 if (err) { 02236 return err; 02237 } 02238 02239 while (true) { 02240 int err = lfs_dir_next(lfs, &cwd, &entry); 02241 if (err && err != LFS_ERR_NOENT) { 02242 return err; 02243 } 02244 02245 if (err == LFS_ERR_NOENT) { 02246 break; 02247 } 02248 02249 if (!(0x80 & entry.d.type) && 02250 memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { 02251 return true; 02252 } 02253 } 02254 } 02255 02256 return false; 02257 } 02258 02259 static int lfs_relocate(lfs_t *lfs, 02260 const lfs_block_t oldpair[2], const lfs_block_t newpair[2]) { 02261 // find parent 02262 lfs_dir_t parent; 02263 lfs_entry_t entry; 02264 int res = lfs_parent(lfs, oldpair, &parent, &entry); 02265 if (res < 0) { 02266 return res; 02267 } 02268 02269 if (res) { 02270 // update disk, this creates a desync 02271 entry.d.u.dir[0] = newpair[0]; 02272 entry.d.u.dir[1] = newpair[1]; 02273 02274 int err = lfs_dir_update(lfs, &parent, &entry, NULL); 02275 if (err) { 02276 return err; 02277 } 02278 02279 // update internal root 02280 if (lfs_paircmp(oldpair, lfs->root) == 0) { 02281 LFS_DEBUG("Relocating root %ld %ld", newpair[0], newpair[1]); 02282 lfs->root[0] = newpair[0]; 02283 lfs->root[1] = newpair[1]; 02284 } 02285 02286 // clean up bad block, which should now be a desync 02287 return lfs_deorphan(lfs); 02288 } 02289 02290 // find pred 02291 res = lfs_pred(lfs, oldpair, &parent); 02292 if (res < 0) { 02293 return res; 02294 } 02295 02296 if (res) { 02297 // just replace bad pair, no desync can occur 02298 parent.d.tail[0] = newpair[0]; 02299 parent.d.tail[1] = newpair[1]; 02300 02301 return lfs_dir_commit(lfs, &parent, NULL, 0); 02302 } 02303 02304 // couldn't find dir, must be new 02305 return 0; 02306 } 02307 02308 int lfs_deorphan(lfs_t *lfs) { 02309 lfs->deorphaned = true; 02310 02311 if (lfs_pairisnull(lfs->root)) { 02312 return 0; 02313 } 02314 02315 lfs_dir_t pdir = {.d.size = 0x80000000}; 02316 lfs_dir_t cwd = {.d.tail[0] = 0, .d.tail[1] = 1}; 02317 02318 // iterate over all directory directory entries 02319 while (!lfs_pairisnull(cwd.d.tail)) { 02320 int err = lfs_dir_fetch(lfs, &cwd, cwd.d.tail); 02321 if (err) { 02322 return err; 02323 } 02324 02325 // check head blocks for orphans 02326 if (!(0x80000000 & pdir.d.size)) { 02327 // check if we have a parent 02328 lfs_dir_t parent; 02329 lfs_entry_t entry; 02330 int res = lfs_parent(lfs, pdir.d.tail, &parent, &entry); 02331 if (res < 0) { 02332 return res; 02333 } 02334 02335 if (!res) { 02336 // we are an orphan 02337 LFS_DEBUG("Found orphan %ld %ld", 02338 pdir.d.tail[0], pdir.d.tail[1]); 02339 02340 pdir.d.tail[0] = cwd.d.tail[0]; 02341 pdir.d.tail[1] = cwd.d.tail[1]; 02342 02343 err = lfs_dir_commit(lfs, &pdir, NULL, 0); 02344 if (err) { 02345 return err; 02346 } 02347 02348 break; 02349 } 02350 02351 if (!lfs_pairsync(entry.d.u.dir, pdir.d.tail)) { 02352 // we have desynced 02353 LFS_DEBUG("Found desync %ld %ld", 02354 entry.d.u.dir[0], entry.d.u.dir[1]); 02355 02356 pdir.d.tail[0] = entry.d.u.dir[0]; 02357 pdir.d.tail[1] = entry.d.u.dir[1]; 02358 02359 err = lfs_dir_commit(lfs, &pdir, NULL, 0); 02360 if (err) { 02361 return err; 02362 } 02363 02364 break; 02365 } 02366 } 02367 02368 // check entries for moves 02369 lfs_entry_t entry; 02370 while (true) { 02371 int err = lfs_dir_next(lfs, &cwd, &entry); 02372 if (err && err != LFS_ERR_NOENT) { 02373 return err; 02374 } 02375 02376 if (err == LFS_ERR_NOENT) { 02377 break; 02378 } 02379 02380 // found moved entry 02381 if (entry.d.type & 0x80) { 02382 int moved = lfs_moved(lfs, &entry.d.u); 02383 if (moved < 0) { 02384 return moved; 02385 } 02386 02387 if (moved) { 02388 LFS_DEBUG("Found move %ld %ld", 02389 entry.d.u.dir[0], entry.d.u.dir[1]); 02390 int err = lfs_dir_remove(lfs, &cwd, &entry); 02391 if (err) { 02392 return err; 02393 } 02394 } else { 02395 LFS_DEBUG("Found partial move %ld %ld", 02396 entry.d.u.dir[0], entry.d.u.dir[1]); 02397 entry.d.type &= ~0x80; 02398 int err = lfs_dir_update(lfs, &cwd, &entry, NULL); 02399 if (err) { 02400 return err; 02401 } 02402 } 02403 } 02404 } 02405 02406 memcpy(&pdir, &cwd, sizeof(pdir)); 02407 } 02408 02409 return 0; 02410 } 02411
Generated on Tue Jul 12 2022 13:02:56 by
