1/* 2 * The little filesystem 3 * 4 * Copyright (c) 2022, The littlefs authors. 5 * Copyright (c) 2017, Arm Limited. All rights reserved. 6 * SPDX-License-Identifier: BSD-3-Clause 7 */ 8#include "lfs.h" 9#include "lfs_util.h" 10 11 12// some constants used throughout the code 13#define LFS_BLOCK_NULL ((lfs_block_t)-1) 14#define LFS_BLOCK_INLINE ((lfs_block_t)-2) 15 16enum { 17 LFS_OK_RELOCATED = 1, 18 LFS_OK_DROPPED = 2, 19 LFS_OK_ORPHANED = 3, 20}; 21 22enum { 23 LFS_CMP_EQ = 0, 24 LFS_CMP_LT = 1, 25 LFS_CMP_GT = 2, 26}; 27 28 29/// Caching block device operations /// 30 31static inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) { 32 // do not zero, cheaper if cache is readonly or only going to be 33 // written with identical data (during relocates) 34 (void)lfs; 35 rcache->block = LFS_BLOCK_NULL; 36} 37 38static inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) { 39 // zero to avoid information leak 40 memset(pcache->buffer, 0xff, lfs->cfg->cache_size); 41 pcache->block = LFS_BLOCK_NULL; 42} 43 44static int lfs_bd_read(lfs_t *lfs, 45 const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, 46 lfs_block_t block, lfs_off_t off, 47 void *buffer, lfs_size_t size) { 48 uint8_t *data = buffer; 49 if (off+size > lfs->cfg->block_size 50 || (lfs->block_count && block >= lfs->block_count)) { 51 return LFS_ERR_CORRUPT; 52 } 53 54 while (size > 0) { 55 lfs_size_t diff = size; 56 57 if (pcache && block == pcache->block && 58 off < pcache->off + pcache->size) { 59 if (off >= pcache->off) { 60 // is already in pcache? 61 diff = lfs_min(diff, pcache->size - (off-pcache->off)); 62 memcpy(data, &pcache->buffer[off-pcache->off], diff); 63 64 data += diff; 65 off += diff; 66 size -= diff; 67 continue; 68 } 69 70 // pcache takes priority 71 diff = lfs_min(diff, pcache->off-off); 72 } 73 74 if (block == rcache->block && 75 off < rcache->off + rcache->size) { 76 if (off >= rcache->off) { 77 // is already in rcache? 78 diff = lfs_min(diff, rcache->size - (off-rcache->off)); 79 memcpy(data, &rcache->buffer[off-rcache->off], diff); 80 81 data += diff; 82 off += diff; 83 size -= diff; 84 continue; 85 } 86 87 // rcache takes priority 88 diff = lfs_min(diff, rcache->off-off); 89 } 90 91 if (size >= hint && off % lfs->cfg->read_size == 0 && 92 size >= lfs->cfg->read_size) { 93 // bypass cache? 94 diff = lfs_aligndown(diff, lfs->cfg->read_size); 95 int err = lfs->cfg->read(lfs->cfg, block, off, data, diff); 96 if (err) { 97 return err; 98 } 99 100 data += diff; 101 off += diff; 102 size -= diff; 103 continue; 104 } 105 106 // load to cache, first condition can no longer fail 107 LFS_ASSERT(!lfs->block_count || block < lfs->block_count); 108 rcache->block = block; 109 rcache->off = lfs_aligndown(off, lfs->cfg->read_size); 110 rcache->size = lfs_min( 111 lfs_min( 112 lfs_alignup(off+hint, lfs->cfg->read_size), 113 lfs->cfg->block_size) 114 - rcache->off, 115 lfs->cfg->cache_size); 116 int err = lfs->cfg->read(lfs->cfg, rcache->block, 117 rcache->off, rcache->buffer, rcache->size); 118 LFS_ASSERT(err <= 0); 119 if (err) { 120 return err; 121 } 122 } 123 124 return 0; 125} 126 127static int lfs_bd_cmp(lfs_t *lfs, 128 const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, 129 lfs_block_t block, lfs_off_t off, 130 const void *buffer, lfs_size_t size) { 131 const uint8_t *data = buffer; 132 lfs_size_t diff = 0; 133 134 for (lfs_off_t i = 0; i < size; i += diff) { 135 uint8_t dat[8]; 136 137 diff = lfs_min(size-i, sizeof(dat)); 138 int err = lfs_bd_read(lfs, 139 pcache, rcache, hint-i, 140 block, off+i, &dat, diff); 141 if (err) { 142 return err; 143 } 144 145 int res = memcmp(dat, data + i, diff); 146 if (res) { 147 return res < 0 ? LFS_CMP_LT : LFS_CMP_GT; 148 } 149 } 150 151 return LFS_CMP_EQ; 152} 153 154static int lfs_bd_crc(lfs_t *lfs, 155 const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, 156 lfs_block_t block, lfs_off_t off, lfs_size_t size, uint32_t *crc) { 157 lfs_size_t diff = 0; 158 159 for (lfs_off_t i = 0; i < size; i += diff) { 160 uint8_t dat[8]; 161 diff = lfs_min(size-i, sizeof(dat)); 162 int err = lfs_bd_read(lfs, 163 pcache, rcache, hint-i, 164 block, off+i, &dat, diff); 165 if (err) { 166 return err; 167 } 168 169 *crc = lfs_crc(*crc, &dat, diff); 170 } 171 172 return 0; 173} 174 175#ifndef LFS_READONLY 176static int lfs_bd_flush(lfs_t *lfs, 177 lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { 178 if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) { 179 LFS_ASSERT(pcache->block < lfs->block_count); 180 lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size); 181 int err = lfs->cfg->prog(lfs->cfg, pcache->block, 182 pcache->off, pcache->buffer, diff); 183 LFS_ASSERT(err <= 0); 184 if (err) { 185 return err; 186 } 187 188 if (validate) { 189 // check data on disk 190 lfs_cache_drop(lfs, rcache); 191 int res = lfs_bd_cmp(lfs, 192 NULL, rcache, diff, 193 pcache->block, pcache->off, pcache->buffer, diff); 194 if (res < 0) { 195 return res; 196 } 197 198 if (res != LFS_CMP_EQ) { 199 return LFS_ERR_CORRUPT; 200 } 201 } 202 203 lfs_cache_zero(lfs, pcache); 204 } 205 206 return 0; 207} 208#endif 209 210#ifndef LFS_READONLY 211static int lfs_bd_sync(lfs_t *lfs, 212 lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) { 213 lfs_cache_drop(lfs, rcache); 214 215 int err = lfs_bd_flush(lfs, pcache, rcache, validate); 216 if (err) { 217 return err; 218 } 219 220 err = lfs->cfg->sync(lfs->cfg); 221 LFS_ASSERT(err <= 0); 222 return err; 223} 224#endif 225 226#ifndef LFS_READONLY 227static int lfs_bd_prog(lfs_t *lfs, 228 lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate, 229 lfs_block_t block, lfs_off_t off, 230 const void *buffer, lfs_size_t size) { 231 const uint8_t *data = buffer; 232 LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->block_count); 233 LFS_ASSERT(off + size <= lfs->cfg->block_size); 234 235 while (size > 0) { 236 if (block == pcache->block && 237 off >= pcache->off && 238 off < pcache->off + lfs->cfg->cache_size) { 239 // already fits in pcache? 240 lfs_size_t diff = lfs_min(size, 241 lfs->cfg->cache_size - (off-pcache->off)); 242 memcpy(&pcache->buffer[off-pcache->off], data, diff); 243 244 data += diff; 245 off += diff; 246 size -= diff; 247 248 pcache->size = lfs_max(pcache->size, off - pcache->off); 249 if (pcache->size == lfs->cfg->cache_size) { 250 // eagerly flush out pcache if we fill up 251 int err = lfs_bd_flush(lfs, pcache, rcache, validate); 252 if (err) { 253 return err; 254 } 255 } 256 257 continue; 258 } 259 260 // pcache must have been flushed, either by programming and 261 // entire block or manually flushing the pcache 262 LFS_ASSERT(pcache->block == LFS_BLOCK_NULL); 263 264 // prepare pcache, first condition can no longer fail 265 pcache->block = block; 266 pcache->off = lfs_aligndown(off, lfs->cfg->prog_size); 267 pcache->size = 0; 268 } 269 270 return 0; 271} 272#endif 273 274#ifndef LFS_READONLY 275static int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) { 276 LFS_ASSERT(block < lfs->block_count); 277 int err = lfs->cfg->erase(lfs->cfg, block); 278 LFS_ASSERT(err <= 0); 279 return err; 280} 281#endif 282 283 284/// Small type-level utilities /// 285// operations on block pairs 286static inline void lfs_pair_swap(lfs_block_t pair[2]) { 287 lfs_block_t t = pair[0]; 288 pair[0] = pair[1]; 289 pair[1] = t; 290} 291 292static inline bool lfs_pair_isnull(const lfs_block_t pair[2]) { 293 return pair[0] == LFS_BLOCK_NULL || pair[1] == LFS_BLOCK_NULL; 294} 295 296static inline int lfs_pair_cmp( 297 const lfs_block_t paira[2], 298 const lfs_block_t pairb[2]) { 299 return !(paira[0] == pairb[0] || paira[1] == pairb[1] || 300 paira[0] == pairb[1] || paira[1] == pairb[0]); 301} 302 303static inline bool lfs_pair_issync( 304 const lfs_block_t paira[2], 305 const lfs_block_t pairb[2]) { 306 return (paira[0] == pairb[0] && paira[1] == pairb[1]) || 307 (paira[0] == pairb[1] && paira[1] == pairb[0]); 308} 309 310static inline void lfs_pair_fromle32(lfs_block_t pair[2]) { 311 pair[0] = lfs_fromle32(pair[0]); 312 pair[1] = lfs_fromle32(pair[1]); 313} 314 315#ifndef LFS_READONLY 316static inline void lfs_pair_tole32(lfs_block_t pair[2]) { 317 pair[0] = lfs_tole32(pair[0]); 318 pair[1] = lfs_tole32(pair[1]); 319} 320#endif 321 322// operations on 32-bit entry tags 323typedef uint32_t lfs_tag_t; 324typedef int32_t lfs_stag_t; 325 326#define LFS_MKTAG(type, id, size) \ 327 (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size)) 328 329#define LFS_MKTAG_IF(cond, type, id, size) \ 330 ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0)) 331 332#define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \ 333 ((cond) ? LFS_MKTAG(type1, id1, size1) : LFS_MKTAG(type2, id2, size2)) 334 335static inline bool lfs_tag_isvalid(lfs_tag_t tag) { 336 return !(tag & 0x80000000); 337} 338 339static inline bool lfs_tag_isdelete(lfs_tag_t tag) { 340 return ((int32_t)(tag << 22) >> 22) == -1; 341} 342 343static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { 344 return (tag & 0x70000000) >> 20; 345} 346 347static inline uint16_t lfs_tag_type2(lfs_tag_t tag) { 348 return (tag & 0x78000000) >> 20; 349} 350 351static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { 352 return (tag & 0x7ff00000) >> 20; 353} 354 355static inline uint8_t lfs_tag_chunk(lfs_tag_t tag) { 356 return (tag & 0x0ff00000) >> 20; 357} 358 359static inline int8_t lfs_tag_splice(lfs_tag_t tag) { 360 return (int8_t)lfs_tag_chunk(tag); 361} 362 363static inline uint16_t lfs_tag_id(lfs_tag_t tag) { 364 return (tag & 0x000ffc00) >> 10; 365} 366 367static inline lfs_size_t lfs_tag_size(lfs_tag_t tag) { 368 return tag & 0x000003ff; 369} 370 371static inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) { 372 return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag)); 373} 374 375// operations on attributes in attribute lists 376struct lfs_mattr { 377 lfs_tag_t tag; 378 const void *buffer; 379}; 380 381struct lfs_diskoff { 382 lfs_block_t block; 383 lfs_off_t off; 384}; 385 386#define LFS_MKATTRS(...) \ 387 (struct lfs_mattr[]){__VA_ARGS__}, \ 388 sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr) 389 390// operations on global state 391static inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) { 392 for (int i = 0; i < 3; i++) { 393 ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; 394 } 395} 396 397static inline bool lfs_gstate_iszero(const lfs_gstate_t *a) { 398 for (int i = 0; i < 3; i++) { 399 if (((uint32_t*)a)[i] != 0) { 400 return false; 401 } 402 } 403 return true; 404} 405 406#ifndef LFS_READONLY 407static inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) { 408 return lfs_tag_size(a->tag); 409} 410 411static inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) { 412 return lfs_tag_size(a->tag) & 0x1ff; 413} 414 415static inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) { 416 return lfs_tag_type1(a->tag); 417} 418#endif 419 420static inline bool lfs_gstate_needssuperblock(const lfs_gstate_t *a) { 421 return lfs_tag_size(a->tag) >> 9; 422} 423 424static inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a, 425 const lfs_block_t *pair) { 426 return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; 427} 428 429static inline void lfs_gstate_fromle32(lfs_gstate_t *a) { 430 a->tag = lfs_fromle32(a->tag); 431 a->pair[0] = lfs_fromle32(a->pair[0]); 432 a->pair[1] = lfs_fromle32(a->pair[1]); 433} 434 435#ifndef LFS_READONLY 436static inline void lfs_gstate_tole32(lfs_gstate_t *a) { 437 a->tag = lfs_tole32(a->tag); 438 a->pair[0] = lfs_tole32(a->pair[0]); 439 a->pair[1] = lfs_tole32(a->pair[1]); 440} 441#endif 442 443// operations on forward-CRCs used to track erased state 444struct lfs_fcrc { 445 lfs_size_t size; 446 uint32_t crc; 447}; 448 449static void lfs_fcrc_fromle32(struct lfs_fcrc *fcrc) { 450 fcrc->size = lfs_fromle32(fcrc->size); 451 fcrc->crc = lfs_fromle32(fcrc->crc); 452} 453 454#ifndef LFS_READONLY 455static void lfs_fcrc_tole32(struct lfs_fcrc *fcrc) { 456 fcrc->size = lfs_tole32(fcrc->size); 457 fcrc->crc = lfs_tole32(fcrc->crc); 458} 459#endif 460 461// other endianness operations 462static void lfs_ctz_fromle32(struct lfs_ctz *ctz) { 463 ctz->head = lfs_fromle32(ctz->head); 464 ctz->size = lfs_fromle32(ctz->size); 465} 466 467#ifndef LFS_READONLY 468static void lfs_ctz_tole32(struct lfs_ctz *ctz) { 469 ctz->head = lfs_tole32(ctz->head); 470 ctz->size = lfs_tole32(ctz->size); 471} 472#endif 473 474static inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) { 475 superblock->version = lfs_fromle32(superblock->version); 476 superblock->block_size = lfs_fromle32(superblock->block_size); 477 superblock->block_count = lfs_fromle32(superblock->block_count); 478 superblock->name_max = lfs_fromle32(superblock->name_max); 479 superblock->file_max = lfs_fromle32(superblock->file_max); 480 superblock->attr_max = lfs_fromle32(superblock->attr_max); 481} 482 483#ifndef LFS_READONLY 484static inline void lfs_superblock_tole32(lfs_superblock_t *superblock) { 485 superblock->version = lfs_tole32(superblock->version); 486 superblock->block_size = lfs_tole32(superblock->block_size); 487 superblock->block_count = lfs_tole32(superblock->block_count); 488 superblock->name_max = lfs_tole32(superblock->name_max); 489 superblock->file_max = lfs_tole32(superblock->file_max); 490 superblock->attr_max = lfs_tole32(superblock->attr_max); 491} 492#endif 493 494#ifndef LFS_NO_ASSERT 495static bool lfs_mlist_isopen(struct lfs_mlist *head, 496 struct lfs_mlist *node) { 497 for (struct lfs_mlist **p = &head; *p; p = &(*p)->next) { 498 if (*p == (struct lfs_mlist*)node) { 499 return true; 500 } 501 } 502 503 return false; 504} 505#endif 506 507static void lfs_mlist_remove(lfs_t *lfs, struct lfs_mlist *mlist) { 508 for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) { 509 if (*p == mlist) { 510 *p = (*p)->next; 511 break; 512 } 513 } 514} 515 516static void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) { 517 mlist->next = lfs->mlist; 518 lfs->mlist = mlist; 519} 520 521// some other filesystem operations 522static uint32_t lfs_fs_disk_version(lfs_t *lfs) { 523 (void)lfs; 524#ifdef LFS_MULTIVERSION 525 if (lfs->cfg->disk_version) { 526 return lfs->cfg->disk_version; 527 } else 528#endif 529 { 530 return LFS_DISK_VERSION; 531 } 532} 533 534static uint16_t lfs_fs_disk_version_major(lfs_t *lfs) { 535 return 0xffff & (lfs_fs_disk_version(lfs) >> 16); 536 537} 538 539static uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) { 540 return 0xffff & (lfs_fs_disk_version(lfs) >> 0); 541} 542 543 544/// Internal operations predeclared here /// 545#ifndef LFS_READONLY 546static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, 547 const struct lfs_mattr *attrs, int attrcount); 548static int lfs_dir_compact(lfs_t *lfs, 549 lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, 550 lfs_mdir_t *source, uint16_t begin, uint16_t end); 551static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, 552 const void *buffer, lfs_size_t size); 553static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, 554 const void *buffer, lfs_size_t size); 555static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file); 556static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file); 557static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file); 558 559static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss); 560static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); 561static void lfs_fs_prepmove(lfs_t *lfs, 562 uint16_t id, const lfs_block_t pair[2]); 563static int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2], 564 lfs_mdir_t *pdir); 565static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], 566 lfs_mdir_t *parent); 567static int lfs_fs_forceconsistency(lfs_t *lfs); 568#endif 569 570static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock); 571 572#ifdef LFS_MIGRATE 573static int lfs1_traverse(lfs_t *lfs, 574 int (*cb)(void*, lfs_block_t), void *data); 575#endif 576 577static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir); 578 579static lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file, 580 void *buffer, lfs_size_t size); 581static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file, 582 void *buffer, lfs_size_t size); 583static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file); 584static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file); 585 586static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs); 587static int lfs_fs_rawtraverse(lfs_t *lfs, 588 int (*cb)(void *data, lfs_block_t block), void *data, 589 bool includeorphans); 590 591static int lfs_deinit(lfs_t *lfs); 592static int lfs_rawunmount(lfs_t *lfs); 593 594 595/// Block allocator /// 596#ifndef LFS_READONLY 597static int lfs_alloc_lookahead(void *p, lfs_block_t block) { 598 lfs_t *lfs = (lfs_t*)p; 599 lfs_block_t off = ((block - lfs->free.off) 600 + lfs->block_count) % lfs->block_count; 601 602 if (off < lfs->free.size) { 603 lfs->free.buffer[off / 32] |= 1U << (off % 32); 604 } 605 606 return 0; 607} 608#endif 609 610// indicate allocated blocks have been committed into the filesystem, this 611// is to prevent blocks from being garbage collected in the middle of a 612// commit operation 613static void lfs_alloc_ack(lfs_t *lfs) { 614 lfs->free.ack = lfs->block_count; 615} 616 617// drop the lookahead buffer, this is done during mounting and failed 618// traversals in order to avoid invalid lookahead state 619static void lfs_alloc_drop(lfs_t *lfs) { 620 lfs->free.size = 0; 621 lfs->free.i = 0; 622 lfs_alloc_ack(lfs); 623} 624 625#ifndef LFS_READONLY 626static int lfs_fs_rawgc(lfs_t *lfs) { 627 // Move free offset at the first unused block (lfs->free.i) 628 // lfs->free.i is equal lfs->free.size when all blocks are used 629 lfs->free.off = (lfs->free.off + lfs->free.i) % lfs->block_count; 630 lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack); 631 lfs->free.i = 0; 632 633 // find mask of free blocks from tree 634 memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); 635 int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true); 636 if (err) { 637 lfs_alloc_drop(lfs); 638 return err; 639 } 640 641 return 0; 642} 643#endif 644 645#ifndef LFS_READONLY 646static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { 647 while (true) { 648 while (lfs->free.i != lfs->free.size) { 649 lfs_block_t off = lfs->free.i; 650 lfs->free.i += 1; 651 lfs->free.ack -= 1; 652 653 if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) { 654 // found a free block 655 *block = (lfs->free.off + off) % lfs->block_count; 656 657 // eagerly find next off so an alloc ack can 658 // discredit old lookahead blocks 659 while (lfs->free.i != lfs->free.size && 660 (lfs->free.buffer[lfs->free.i / 32] 661 & (1U << (lfs->free.i % 32)))) { 662 lfs->free.i += 1; 663 lfs->free.ack -= 1; 664 } 665 666 return 0; 667 } 668 } 669 670 // check if we have looked at all blocks since last ack 671 if (lfs->free.ack == 0) { 672 LFS_ERROR("No more free space %"PRIu32, 673 lfs->free.i + lfs->free.off); 674 return LFS_ERR_NOSPC; 675 } 676 677 int err = lfs_fs_rawgc(lfs); 678 if(err) { 679 return err; 680 } 681 } 682} 683#endif 684 685/// Metadata pair and directory operations /// 686static lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir, 687 lfs_tag_t gmask, lfs_tag_t gtag, 688 lfs_off_t goff, void *gbuffer, lfs_size_t gsize) { 689 lfs_off_t off = dir->off; 690 lfs_tag_t ntag = dir->etag; 691 lfs_stag_t gdiff = 0; 692 693 if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair) && 694 lfs_tag_id(gmask) != 0 && 695 lfs_tag_id(lfs->gdisk.tag) <= lfs_tag_id(gtag)) { 696 // synthetic moves 697 gdiff -= LFS_MKTAG(0, 1, 0); 698 } 699 700 // iterate over dir block backwards (for faster lookups) 701 while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) { 702 off -= lfs_tag_dsize(ntag); 703 lfs_tag_t tag = ntag; 704 int err = lfs_bd_read(lfs, 705 NULL, &lfs->rcache, sizeof(ntag), 706 dir->pair[0], off, &ntag, sizeof(ntag)); 707 if (err) { 708 return err; 709 } 710 711 ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff; 712 713 if (lfs_tag_id(gmask) != 0 && 714 lfs_tag_type1(tag) == LFS_TYPE_SPLICE && 715 lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) { 716 if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) | 717 (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) { 718 // found where we were created 719 return LFS_ERR_NOENT; 720 } 721 722 // move around splices 723 gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0); 724 } 725 726 if ((gmask & tag) == (gmask & (gtag - gdiff))) { 727 if (lfs_tag_isdelete(tag)) { 728 return LFS_ERR_NOENT; 729 } 730 731 lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize); 732 err = lfs_bd_read(lfs, 733 NULL, &lfs->rcache, diff, 734 dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); 735 if (err) { 736 return err; 737 } 738 739 memset((uint8_t*)gbuffer + diff, 0, gsize - diff); 740 741 return tag + gdiff; 742 } 743 } 744 745 return LFS_ERR_NOENT; 746} 747 748static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, 749 lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) { 750 return lfs_dir_getslice(lfs, dir, 751 gmask, gtag, 752 0, buffer, lfs_tag_size(gtag)); 753} 754 755static int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir, 756 const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint, 757 lfs_tag_t gmask, lfs_tag_t gtag, 758 lfs_off_t off, void *buffer, lfs_size_t size) { 759 uint8_t *data = buffer; 760 if (off+size > lfs->cfg->block_size) { 761 return LFS_ERR_CORRUPT; 762 } 763 764 while (size > 0) { 765 lfs_size_t diff = size; 766 767 if (pcache && pcache->block == LFS_BLOCK_INLINE && 768 off < pcache->off + pcache->size) { 769 if (off >= pcache->off) { 770 // is already in pcache? 771 diff = lfs_min(diff, pcache->size - (off-pcache->off)); 772 memcpy(data, &pcache->buffer[off-pcache->off], diff); 773 774 data += diff; 775 off += diff; 776 size -= diff; 777 continue; 778 } 779 780 // pcache takes priority 781 diff = lfs_min(diff, pcache->off-off); 782 } 783 784 if (rcache->block == LFS_BLOCK_INLINE && 785 off < rcache->off + rcache->size) { 786 if (off >= rcache->off) { 787 // is already in rcache? 788 diff = lfs_min(diff, rcache->size - (off-rcache->off)); 789 memcpy(data, &rcache->buffer[off-rcache->off], diff); 790 791 data += diff; 792 off += diff; 793 size -= diff; 794 continue; 795 } 796 797 // rcache takes priority 798 diff = lfs_min(diff, rcache->off-off); 799 } 800 801 // load to cache, first condition can no longer fail 802 rcache->block = LFS_BLOCK_INLINE; 803 rcache->off = lfs_aligndown(off, lfs->cfg->read_size); 804 rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size), 805 lfs->cfg->cache_size); 806 int err = lfs_dir_getslice(lfs, dir, gmask, gtag, 807 rcache->off, rcache->buffer, rcache->size); 808 if (err < 0) { 809 return err; 810 } 811 } 812 813 return 0; 814} 815 816#ifndef LFS_READONLY 817static int lfs_dir_traverse_filter(void *p, 818 lfs_tag_t tag, const void *buffer) { 819 lfs_tag_t *filtertag = p; 820 (void)buffer; 821 822 // which mask depends on unique bit in tag structure 823 uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0)) 824 ? LFS_MKTAG(0x7ff, 0x3ff, 0) 825 : LFS_MKTAG(0x700, 0x3ff, 0); 826 827 // check for redundancy 828 if ((mask & tag) == (mask & *filtertag) || 829 lfs_tag_isdelete(*filtertag) || 830 (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == ( 831 LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | 832 (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) { 833 *filtertag = LFS_MKTAG(LFS_FROM_NOOP, 0, 0); 834 return true; 835 } 836 837 // check if we need to adjust for created/deleted tags 838 if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE && 839 lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) { 840 *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); 841 } 842 843 return false; 844} 845#endif 846 847#ifndef LFS_READONLY 848// maximum recursive depth of lfs_dir_traverse, the deepest call: 849// 850// traverse with commit 851// '-> traverse with move 852// '-> traverse with filter 853// 854#define LFS_DIR_TRAVERSE_DEPTH 3 855 856struct lfs_dir_traverse { 857 const lfs_mdir_t *dir; 858 lfs_off_t off; 859 lfs_tag_t ptag; 860 const struct lfs_mattr *attrs; 861 int attrcount; 862 863 lfs_tag_t tmask; 864 lfs_tag_t ttag; 865 uint16_t begin; 866 uint16_t end; 867 int16_t diff; 868 869 int (*cb)(void *data, lfs_tag_t tag, const void *buffer); 870 void *data; 871 872 lfs_tag_t tag; 873 const void *buffer; 874 struct lfs_diskoff disk; 875}; 876 877static int lfs_dir_traverse(lfs_t *lfs, 878 const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, 879 const struct lfs_mattr *attrs, int attrcount, 880 lfs_tag_t tmask, lfs_tag_t ttag, 881 uint16_t begin, uint16_t end, int16_t diff, 882 int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { 883 // This function in inherently recursive, but bounded. To allow tool-based 884 // analysis without unnecessary code-cost we use an explicit stack 885 struct lfs_dir_traverse stack[LFS_DIR_TRAVERSE_DEPTH-1]; 886 unsigned sp = 0; 887 int res; 888 889 // iterate over directory and attrs 890 lfs_tag_t tag; 891 const void *buffer; 892 struct lfs_diskoff disk = {0}; 893 while (true) { 894 { 895 if (off+lfs_tag_dsize(ptag) < dir->off) { 896 off += lfs_tag_dsize(ptag); 897 int err = lfs_bd_read(lfs, 898 NULL, &lfs->rcache, sizeof(tag), 899 dir->pair[0], off, &tag, sizeof(tag)); 900 if (err) { 901 return err; 902 } 903 904 tag = (lfs_frombe32(tag) ^ ptag) | 0x80000000; 905 disk.block = dir->pair[0]; 906 disk.off = off+sizeof(lfs_tag_t); 907 buffer = &disk; 908 ptag = tag; 909 } else if (attrcount > 0) { 910 tag = attrs[0].tag; 911 buffer = attrs[0].buffer; 912 attrs += 1; 913 attrcount -= 1; 914 } else { 915 // finished traversal, pop from stack? 916 res = 0; 917 break; 918 } 919 920 // do we need to filter? 921 lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); 922 if ((mask & tmask & tag) != (mask & tmask & ttag)) { 923 continue; 924 } 925 926 if (lfs_tag_id(tmask) != 0) { 927 LFS_ASSERT(sp < LFS_DIR_TRAVERSE_DEPTH); 928 // recurse, scan for duplicates, and update tag based on 929 // creates/deletes 930 stack[sp] = (struct lfs_dir_traverse){ 931 .dir = dir, 932 .off = off, 933 .ptag = ptag, 934 .attrs = attrs, 935 .attrcount = attrcount, 936 .tmask = tmask, 937 .ttag = ttag, 938 .begin = begin, 939 .end = end, 940 .diff = diff, 941 .cb = cb, 942 .data = data, 943 .tag = tag, 944 .buffer = buffer, 945 .disk = disk, 946 }; 947 sp += 1; 948 949 tmask = 0; 950 ttag = 0; 951 begin = 0; 952 end = 0; 953 diff = 0; 954 cb = lfs_dir_traverse_filter; 955 data = &stack[sp-1].tag; 956 continue; 957 } 958 } 959 960popped: 961 // in filter range? 962 if (lfs_tag_id(tmask) != 0 && 963 !(lfs_tag_id(tag) >= begin && lfs_tag_id(tag) < end)) { 964 continue; 965 } 966 967 // handle special cases for mcu-side operations 968 if (lfs_tag_type3(tag) == LFS_FROM_NOOP) { 969 // do nothing 970 } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) { 971 // Without this condition, lfs_dir_traverse can exhibit an 972 // extremely expensive O(n^3) of nested loops when renaming. 973 // This happens because lfs_dir_traverse tries to filter tags by 974 // the tags in the source directory, triggering a second 975 // lfs_dir_traverse with its own filter operation. 976 // 977 // traverse with commit 978 // '-> traverse with filter 979 // '-> traverse with move 980 // '-> traverse with filter 981 // 982 // However we don't actually care about filtering the second set of 983 // tags, since duplicate tags have no effect when filtering. 984 // 985 // This check skips this unnecessary recursive filtering explicitly, 986 // reducing this runtime from O(n^3) to O(n^2). 987 if (cb == lfs_dir_traverse_filter) { 988 continue; 989 } 990 991 // recurse into move 992 stack[sp] = (struct lfs_dir_traverse){ 993 .dir = dir, 994 .off = off, 995 .ptag = ptag, 996 .attrs = attrs, 997 .attrcount = attrcount, 998 .tmask = tmask, 999 .ttag = ttag, 1000 .begin = begin, 1001 .end = end, 1002 .diff = diff, 1003 .cb = cb, 1004 .data = data, 1005 .tag = LFS_MKTAG(LFS_FROM_NOOP, 0, 0), 1006 }; 1007 sp += 1; 1008 1009 uint16_t fromid = lfs_tag_size(tag); 1010 uint16_t toid = lfs_tag_id(tag); 1011 dir = buffer; 1012 off = 0; 1013 ptag = 0xffffffff; 1014 attrs = NULL; 1015 attrcount = 0; 1016 tmask = LFS_MKTAG(0x600, 0x3ff, 0); 1017 ttag = LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0); 1018 begin = fromid; 1019 end = fromid+1; 1020 diff = toid-fromid+diff; 1021 } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) { 1022 for (unsigned i = 0; i < lfs_tag_size(tag); i++) { 1023 const struct lfs_attr *a = buffer; 1024 res = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type, 1025 lfs_tag_id(tag) + diff, a[i].size), a[i].buffer); 1026 if (res < 0) { 1027 return res; 1028 } 1029 1030 if (res) { 1031 break; 1032 } 1033 } 1034 } else { 1035 res = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer); 1036 if (res < 0) { 1037 return res; 1038 } 1039 1040 if (res) { 1041 break; 1042 } 1043 } 1044 } 1045 1046 if (sp > 0) { 1047 // pop from the stack and return, fortunately all pops share 1048 // a destination 1049 dir = stack[sp-1].dir; 1050 off = stack[sp-1].off; 1051 ptag = stack[sp-1].ptag; 1052 attrs = stack[sp-1].attrs; 1053 attrcount = stack[sp-1].attrcount; 1054 tmask = stack[sp-1].tmask; 1055 ttag = stack[sp-1].ttag; 1056 begin = stack[sp-1].begin; 1057 end = stack[sp-1].end; 1058 diff = stack[sp-1].diff; 1059 cb = stack[sp-1].cb; 1060 data = stack[sp-1].data; 1061 tag = stack[sp-1].tag; 1062 buffer = stack[sp-1].buffer; 1063 disk = stack[sp-1].disk; 1064 sp -= 1; 1065 goto popped; 1066 } else { 1067 return res; 1068 } 1069} 1070#endif 1071 1072static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, 1073 lfs_mdir_t *dir, const lfs_block_t pair[2], 1074 lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id, 1075 int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { 1076 // we can find tag very efficiently during a fetch, since we're already 1077 // scanning the entire directory 1078 lfs_stag_t besttag = -1; 1079 1080 // if either block address is invalid we return LFS_ERR_CORRUPT here, 1081 // otherwise later writes to the pair could fail 1082 if (lfs->block_count 1083 && (pair[0] >= lfs->block_count || pair[1] >= lfs->block_count)) { 1084 return LFS_ERR_CORRUPT; 1085 } 1086 1087 // find the block with the most recent revision 1088 uint32_t revs[2] = {0, 0}; 1089 int r = 0; 1090 for (int i = 0; i < 2; i++) { 1091 int err = lfs_bd_read(lfs, 1092 NULL, &lfs->rcache, sizeof(revs[i]), 1093 pair[i], 0, &revs[i], sizeof(revs[i])); 1094 revs[i] = lfs_fromle32(revs[i]); 1095 if (err && err != LFS_ERR_CORRUPT) { 1096 return err; 1097 } 1098 1099 if (err != LFS_ERR_CORRUPT && 1100 lfs_scmp(revs[i], revs[(i+1)%2]) > 0) { 1101 r = i; 1102 } 1103 } 1104 1105 dir->pair[0] = pair[(r+0)%2]; 1106 dir->pair[1] = pair[(r+1)%2]; 1107 dir->rev = revs[(r+0)%2]; 1108 dir->off = 0; // nonzero = found some commits 1109 1110 // now scan tags to fetch the actual dir and find possible match 1111 for (int i = 0; i < 2; i++) { 1112 lfs_off_t off = 0; 1113 lfs_tag_t ptag = 0xffffffff; 1114 1115 uint16_t tempcount = 0; 1116 lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; 1117 bool tempsplit = false; 1118 lfs_stag_t tempbesttag = besttag; 1119 1120 // assume not erased until proven otherwise 1121 bool maybeerased = false; 1122 bool hasfcrc = false; 1123 struct lfs_fcrc fcrc; 1124 1125 dir->rev = lfs_tole32(dir->rev); 1126 uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); 1127 dir->rev = lfs_fromle32(dir->rev); 1128 1129 while (true) { 1130 // extract next tag 1131 lfs_tag_t tag; 1132 off += lfs_tag_dsize(ptag); 1133 int err = lfs_bd_read(lfs, 1134 NULL, &lfs->rcache, lfs->cfg->block_size, 1135 dir->pair[0], off, &tag, sizeof(tag)); 1136 if (err) { 1137 if (err == LFS_ERR_CORRUPT) { 1138 // can't continue? 1139 break; 1140 } 1141 return err; 1142 } 1143 1144 crc = lfs_crc(crc, &tag, sizeof(tag)); 1145 tag = lfs_frombe32(tag) ^ ptag; 1146 1147 // next commit not yet programmed? 1148 if (!lfs_tag_isvalid(tag)) { 1149 // we only might be erased if the last tag was a crc 1150 maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC); 1151 break; 1152 // out of range? 1153 } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) { 1154 break; 1155 } 1156 1157 ptag = tag; 1158 1159 if (lfs_tag_type2(tag) == LFS_TYPE_CCRC) { 1160 // check the crc attr 1161 uint32_t dcrc; 1162 err = lfs_bd_read(lfs, 1163 NULL, &lfs->rcache, lfs->cfg->block_size, 1164 dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); 1165 if (err) { 1166 if (err == LFS_ERR_CORRUPT) { 1167 break; 1168 } 1169 return err; 1170 } 1171 dcrc = lfs_fromle32(dcrc); 1172 1173 if (crc != dcrc) { 1174 break; 1175 } 1176 1177 // reset the next bit if we need to 1178 ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31; 1179 1180 // toss our crc into the filesystem seed for 1181 // pseudorandom numbers, note we use another crc here 1182 // as a collection function because it is sufficiently 1183 // random and convenient 1184 lfs->seed = lfs_crc(lfs->seed, &crc, sizeof(crc)); 1185 1186 // update with what's found so far 1187 besttag = tempbesttag; 1188 dir->off = off + lfs_tag_dsize(tag); 1189 dir->etag = ptag; 1190 dir->count = tempcount; 1191 dir->tail[0] = temptail[0]; 1192 dir->tail[1] = temptail[1]; 1193 dir->split = tempsplit; 1194 1195 // reset crc, hasfcrc 1196 crc = 0xffffffff; 1197 continue; 1198 } 1199 1200 // crc the entry first, hopefully leaving it in the cache 1201 err = lfs_bd_crc(lfs, 1202 NULL, &lfs->rcache, lfs->cfg->block_size, 1203 dir->pair[0], off+sizeof(tag), 1204 lfs_tag_dsize(tag)-sizeof(tag), &crc); 1205 if (err) { 1206 if (err == LFS_ERR_CORRUPT) { 1207 break; 1208 } 1209 return err; 1210 } 1211 1212 // directory modification tags? 1213 if (lfs_tag_type1(tag) == LFS_TYPE_NAME) { 1214 // increase count of files if necessary 1215 if (lfs_tag_id(tag) >= tempcount) { 1216 tempcount = lfs_tag_id(tag) + 1; 1217 } 1218 } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) { 1219 tempcount += lfs_tag_splice(tag); 1220 1221 if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) | 1222 (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) { 1223 tempbesttag |= 0x80000000; 1224 } else if (tempbesttag != -1 && 1225 lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { 1226 tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0); 1227 } 1228 } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { 1229 tempsplit = (lfs_tag_chunk(tag) & 1); 1230 1231 err = lfs_bd_read(lfs, 1232 NULL, &lfs->rcache, lfs->cfg->block_size, 1233 dir->pair[0], off+sizeof(tag), &temptail, 8); 1234 if (err) { 1235 if (err == LFS_ERR_CORRUPT) { 1236 break; 1237 } 1238 return err; 1239 } 1240 lfs_pair_fromle32(temptail); 1241 } else if (lfs_tag_type3(tag) == LFS_TYPE_FCRC) { 1242 err = lfs_bd_read(lfs, 1243 NULL, &lfs->rcache, lfs->cfg->block_size, 1244 dir->pair[0], off+sizeof(tag), 1245 &fcrc, sizeof(fcrc)); 1246 if (err) { 1247 if (err == LFS_ERR_CORRUPT) { 1248 break; 1249 } 1250 } 1251 1252 lfs_fcrc_fromle32(&fcrc); 1253 hasfcrc = true; 1254 } 1255 1256 // found a match for our fetcher? 1257 if ((fmask & tag) == (fmask & ftag)) { 1258 int res = cb(data, tag, &(struct lfs_diskoff){ 1259 dir->pair[0], off+sizeof(tag)}); 1260 if (res < 0) { 1261 if (res == LFS_ERR_CORRUPT) { 1262 break; 1263 } 1264 return res; 1265 } 1266 1267 if (res == LFS_CMP_EQ) { 1268 // found a match 1269 tempbesttag = tag; 1270 } else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == 1271 (LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) { 1272 // found an identical tag, but contents didn't match 1273 // this must mean that our besttag has been overwritten 1274 tempbesttag = -1; 1275 } else if (res == LFS_CMP_GT && 1276 lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) { 1277 // found a greater match, keep track to keep things sorted 1278 tempbesttag = tag | 0x80000000; 1279 } 1280 } 1281 } 1282 1283 // found no valid commits? 1284 if (dir->off == 0) { 1285 // try the other block? 1286 lfs_pair_swap(dir->pair); 1287 dir->rev = revs[(r+1)%2]; 1288 continue; 1289 } 1290 1291 // did we end on a valid commit? we may have an erased block 1292 dir->erased = false; 1293 if (maybeerased && dir->off % lfs->cfg->prog_size == 0) { 1294 #ifdef LFS_MULTIVERSION 1295 // note versions < lfs2.1 did not have fcrc tags, if 1296 // we're < lfs2.1 treat missing fcrc as erased data 1297 // 1298 // we don't strictly need to do this, but otherwise writing 1299 // to lfs2.0 disks becomes very inefficient 1300 if (lfs_fs_disk_version(lfs) < 0x00020001) { 1301 dir->erased = true; 1302 1303 } else 1304 #endif 1305 if (hasfcrc) { 1306 // check for an fcrc matching the next prog's erased state, if 1307 // this failed most likely a previous prog was interrupted, we 1308 // need a new erase 1309 uint32_t fcrc_ = 0xffffffff; 1310 int err = lfs_bd_crc(lfs, 1311 NULL, &lfs->rcache, lfs->cfg->block_size, 1312 dir->pair[0], dir->off, fcrc.size, &fcrc_); 1313 if (err && err != LFS_ERR_CORRUPT) { 1314 return err; 1315 } 1316 1317 // found beginning of erased part? 1318 dir->erased = (fcrc_ == fcrc.crc); 1319 } 1320 } 1321 1322 // synthetic move 1323 if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) { 1324 if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) { 1325 besttag |= 0x80000000; 1326 } else if (besttag != -1 && 1327 lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) { 1328 besttag -= LFS_MKTAG(0, 1, 0); 1329 } 1330 } 1331 1332 // found tag? or found best id? 1333 if (id) { 1334 *id = lfs_min(lfs_tag_id(besttag), dir->count); 1335 } 1336 1337 if (lfs_tag_isvalid(besttag)) { 1338 return besttag; 1339 } else if (lfs_tag_id(besttag) < dir->count) { 1340 return LFS_ERR_NOENT; 1341 } else { 1342 return 0; 1343 } 1344 } 1345 1346 LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", 1347 dir->pair[0], dir->pair[1]); 1348 return LFS_ERR_CORRUPT; 1349} 1350 1351static int lfs_dir_fetch(lfs_t *lfs, 1352 lfs_mdir_t *dir, const lfs_block_t pair[2]) { 1353 // note, mask=-1, tag=-1 can never match a tag since this 1354 // pattern has the invalid bit set 1355 return (int)lfs_dir_fetchmatch(lfs, dir, pair, 1356 (lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL); 1357} 1358 1359static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, 1360 lfs_gstate_t *gstate) { 1361 lfs_gstate_t temp; 1362 lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), 1363 LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); 1364 if (res < 0 && res != LFS_ERR_NOENT) { 1365 return res; 1366 } 1367 1368 if (res != LFS_ERR_NOENT) { 1369 // xor together to find resulting gstate 1370 lfs_gstate_fromle32(&temp); 1371 lfs_gstate_xor(gstate, &temp); 1372 } 1373 1374 return 0; 1375} 1376 1377static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir, 1378 uint16_t id, struct lfs_info *info) { 1379 if (id == 0x3ff) { 1380 // special case for root 1381 strcpy(info->name, "/"); 1382 info->type = LFS_TYPE_DIR; 1383 return 0; 1384 } 1385 1386 lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0), 1387 LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name); 1388 if (tag < 0) { 1389 return (int)tag; 1390 } 1391 1392 info->type = lfs_tag_type3(tag); 1393 1394 struct lfs_ctz ctz; 1395 tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), 1396 LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); 1397 if (tag < 0) { 1398 return (int)tag; 1399 } 1400 lfs_ctz_fromle32(&ctz); 1401 1402 if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { 1403 info->size = ctz.size; 1404 } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { 1405 info->size = lfs_tag_size(tag); 1406 } 1407 1408 return 0; 1409} 1410 1411struct lfs_dir_find_match { 1412 lfs_t *lfs; 1413 const void *name; 1414 lfs_size_t size; 1415}; 1416 1417static int lfs_dir_find_match(void *data, 1418 lfs_tag_t tag, const void *buffer) { 1419 struct lfs_dir_find_match *name = data; 1420 lfs_t *lfs = name->lfs; 1421 const struct lfs_diskoff *disk = buffer; 1422 1423 // compare with disk 1424 lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag)); 1425 int res = lfs_bd_cmp(lfs, 1426 NULL, &lfs->rcache, diff, 1427 disk->block, disk->off, name->name, diff); 1428 if (res != LFS_CMP_EQ) { 1429 return res; 1430 } 1431 1432 // only equal if our size is still the same 1433 if (name->size != lfs_tag_size(tag)) { 1434 return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT; 1435 } 1436 1437 // found a match! 1438 return LFS_CMP_EQ; 1439} 1440 1441static lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, 1442 const char **path, uint16_t *id) { 1443 // we reduce path to a single name if we can find it 1444 const char *name = *path; 1445 if (id) { 1446 *id = 0x3ff; 1447 } 1448 1449 // default to root dir 1450 lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0); 1451 dir->tail[0] = lfs->root[0]; 1452 dir->tail[1] = lfs->root[1]; 1453 1454 while (true) { 1455nextname: 1456 // skip slashes 1457 name += strspn(name, "/"); 1458 lfs_size_t namelen = strcspn(name, "/"); 1459 1460 // skip '.' and root '..' 1461 if ((namelen == 1 && memcmp(name, ".", 1) == 0) || 1462 (namelen == 2 && memcmp(name, "..", 2) == 0)) { 1463 name += namelen; 1464 goto nextname; 1465 } 1466 1467 // skip if matched by '..' in name 1468 const char *suffix = name + namelen; 1469 lfs_size_t sufflen; 1470 int depth = 1; 1471 while (true) { 1472 suffix += strspn(suffix, "/"); 1473 sufflen = strcspn(suffix, "/"); 1474 if (sufflen == 0) { 1475 break; 1476 } 1477 1478 if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { 1479 depth -= 1; 1480 if (depth == 0) { 1481 name = suffix + sufflen; 1482 goto nextname; 1483 } 1484 } else { 1485 depth += 1; 1486 } 1487 1488 suffix += sufflen; 1489 } 1490 1491 // found path 1492 if (name[0] == '\0') { 1493 return tag; 1494 } 1495 1496 // update what we've found so far 1497 *path = name; 1498 1499 // only continue if we hit a directory 1500 if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { 1501 return LFS_ERR_NOTDIR; 1502 } 1503 1504 // grab the entry data 1505 if (lfs_tag_id(tag) != 0x3ff) { 1506 lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0), 1507 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail); 1508 if (res < 0) { 1509 return res; 1510 } 1511 lfs_pair_fromle32(dir->tail); 1512 } 1513 1514 // find entry matching name 1515 while (true) { 1516 tag = lfs_dir_fetchmatch(lfs, dir, dir->tail, 1517 LFS_MKTAG(0x780, 0, 0), 1518 LFS_MKTAG(LFS_TYPE_NAME, 0, namelen), 1519 // are we last name? 1520 (strchr(name, '/') == NULL) ? id : NULL, 1521 lfs_dir_find_match, &(struct lfs_dir_find_match){ 1522 lfs, name, namelen}); 1523 if (tag < 0) { 1524 return tag; 1525 } 1526 1527 if (tag) { 1528 break; 1529 } 1530 1531 if (!dir->split) { 1532 return LFS_ERR_NOENT; 1533 } 1534 } 1535 1536 // to next name 1537 name += namelen; 1538 } 1539} 1540 1541// commit logic 1542struct lfs_commit { 1543 lfs_block_t block; 1544 lfs_off_t off; 1545 lfs_tag_t ptag; 1546 uint32_t crc; 1547 1548 lfs_off_t begin; 1549 lfs_off_t end; 1550}; 1551 1552#ifndef LFS_READONLY 1553static int lfs_dir_commitprog(lfs_t *lfs, struct lfs_commit *commit, 1554 const void *buffer, lfs_size_t size) { 1555 int err = lfs_bd_prog(lfs, 1556 &lfs->pcache, &lfs->rcache, false, 1557 commit->block, commit->off , 1558 (const uint8_t*)buffer, size); 1559 if (err) { 1560 return err; 1561 } 1562 1563 commit->crc = lfs_crc(commit->crc, buffer, size); 1564 commit->off += size; 1565 return 0; 1566} 1567#endif 1568 1569#ifndef LFS_READONLY 1570static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, 1571 lfs_tag_t tag, const void *buffer) { 1572 // check if we fit 1573 lfs_size_t dsize = lfs_tag_dsize(tag); 1574 if (commit->off + dsize > commit->end) { 1575 return LFS_ERR_NOSPC; 1576 } 1577 1578 // write out tag 1579 lfs_tag_t ntag = lfs_tobe32((tag & 0x7fffffff) ^ commit->ptag); 1580 int err = lfs_dir_commitprog(lfs, commit, &ntag, sizeof(ntag)); 1581 if (err) { 1582 return err; 1583 } 1584 1585 if (!(tag & 0x80000000)) { 1586 // from memory 1587 err = lfs_dir_commitprog(lfs, commit, buffer, dsize-sizeof(tag)); 1588 if (err) { 1589 return err; 1590 } 1591 } else { 1592 // from disk 1593 const struct lfs_diskoff *disk = buffer; 1594 for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) { 1595 // rely on caching to make this efficient 1596 uint8_t dat; 1597 err = lfs_bd_read(lfs, 1598 NULL, &lfs->rcache, dsize-sizeof(tag)-i, 1599 disk->block, disk->off+i, &dat, 1); 1600 if (err) { 1601 return err; 1602 } 1603 1604 err = lfs_dir_commitprog(lfs, commit, &dat, 1); 1605 if (err) { 1606 return err; 1607 } 1608 } 1609 } 1610 1611 commit->ptag = tag & 0x7fffffff; 1612 return 0; 1613} 1614#endif 1615 1616#ifndef LFS_READONLY 1617 1618static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { 1619 // align to program units 1620 // 1621 // this gets a bit complex as we have two types of crcs: 1622 // - 5-word crc with fcrc to check following prog (middle of block) 1623 // - 2-word crc with no following prog (end of block) 1624 const lfs_off_t end = lfs_alignup( 1625 lfs_min(commit->off + 5*sizeof(uint32_t), lfs->cfg->block_size), 1626 lfs->cfg->prog_size); 1627 1628 lfs_off_t off1 = 0; 1629 uint32_t crc1 = 0; 1630 1631 // create crc tags to fill up remainder of commit, note that 1632 // padding is not crced, which lets fetches skip padding but 1633 // makes committing a bit more complicated 1634 while (commit->off < end) { 1635 lfs_off_t noff = ( 1636 lfs_min(end - (commit->off+sizeof(lfs_tag_t)), 0x3fe) 1637 + (commit->off+sizeof(lfs_tag_t))); 1638 // too large for crc tag? need padding commits 1639 if (noff < end) { 1640 noff = lfs_min(noff, end - 5*sizeof(uint32_t)); 1641 } 1642 1643 // space for fcrc? 1644 uint8_t eperturb = (uint8_t)-1; 1645 if (noff >= end && noff <= lfs->cfg->block_size - lfs->cfg->prog_size) { 1646 // first read the leading byte, this always contains a bit 1647 // we can perturb to avoid writes that don't change the fcrc 1648 int err = lfs_bd_read(lfs, 1649 NULL, &lfs->rcache, lfs->cfg->prog_size, 1650 commit->block, noff, &eperturb, 1); 1651 if (err && err != LFS_ERR_CORRUPT) { 1652 return err; 1653 } 1654 1655 #ifdef LFS_MULTIVERSION 1656 // unfortunately fcrcs break mdir fetching < lfs2.1, so only write 1657 // these if we're a >= lfs2.1 filesystem 1658 if (lfs_fs_disk_version(lfs) <= 0x00020000) { 1659 // don't write fcrc 1660 } else 1661 #endif 1662 { 1663 // find the expected fcrc, don't bother avoiding a reread 1664 // of the eperturb, it should still be in our cache 1665 struct lfs_fcrc fcrc = { 1666 .size = lfs->cfg->prog_size, 1667 .crc = 0xffffffff 1668 }; 1669 err = lfs_bd_crc(lfs, 1670 NULL, &lfs->rcache, lfs->cfg->prog_size, 1671 commit->block, noff, fcrc.size, &fcrc.crc); 1672 if (err && err != LFS_ERR_CORRUPT) { 1673 return err; 1674 } 1675 1676 lfs_fcrc_tole32(&fcrc); 1677 err = lfs_dir_commitattr(lfs, commit, 1678 LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)), 1679 &fcrc); 1680 if (err) { 1681 return err; 1682 } 1683 } 1684 } 1685 1686 // build commit crc 1687 struct { 1688 lfs_tag_t tag; 1689 uint32_t crc; 1690 } ccrc; 1691 lfs_tag_t ntag = LFS_MKTAG( 1692 LFS_TYPE_CCRC + (((uint8_t)~eperturb) >> 7), 0x3ff, 1693 noff - (commit->off+sizeof(lfs_tag_t))); 1694 ccrc.tag = lfs_tobe32(ntag ^ commit->ptag); 1695 commit->crc = lfs_crc(commit->crc, &ccrc.tag, sizeof(lfs_tag_t)); 1696 ccrc.crc = lfs_tole32(commit->crc); 1697 1698 int err = lfs_bd_prog(lfs, 1699 &lfs->pcache, &lfs->rcache, false, 1700 commit->block, commit->off, &ccrc, sizeof(ccrc)); 1701 if (err) { 1702 return err; 1703 } 1704 1705 // keep track of non-padding checksum to verify 1706 if (off1 == 0) { 1707 off1 = commit->off + sizeof(lfs_tag_t); 1708 crc1 = commit->crc; 1709 } 1710 1711 commit->off = noff; 1712 // perturb valid bit? 1713 commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24); 1714 // reset crc for next commit 1715 commit->crc = 0xffffffff; 1716 1717 // manually flush here since we don't prog the padding, this confuses 1718 // the caching layer 1719 if (noff >= end || noff >= lfs->pcache.off + lfs->cfg->cache_size) { 1720 // flush buffers 1721 int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false); 1722 if (err) { 1723 return err; 1724 } 1725 } 1726 } 1727 1728 // successful commit, check checksums to make sure 1729 // 1730 // note that we don't need to check padding commits, worst 1731 // case if they are corrupted we would have had to compact anyways 1732 lfs_off_t off = commit->begin; 1733 uint32_t crc = 0xffffffff; 1734 int err = lfs_bd_crc(lfs, 1735 NULL, &lfs->rcache, off1+sizeof(uint32_t), 1736 commit->block, off, off1-off, &crc); 1737 if (err) { 1738 return err; 1739 } 1740 1741 // check non-padding commits against known crc 1742 if (crc != crc1) { 1743 return LFS_ERR_CORRUPT; 1744 } 1745 1746 // make sure to check crc in case we happen to pick 1747 // up an unrelated crc (frozen block?) 1748 err = lfs_bd_crc(lfs, 1749 NULL, &lfs->rcache, sizeof(uint32_t), 1750 commit->block, off1, sizeof(uint32_t), &crc); 1751 if (err) { 1752 return err; 1753 } 1754 1755 if (crc != 0) { 1756 return LFS_ERR_CORRUPT; 1757 } 1758 1759 return 0; 1760} 1761#endif 1762 1763#ifndef LFS_READONLY 1764static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { 1765 // allocate pair of dir blocks (backwards, so we write block 1 first) 1766 for (int i = 0; i < 2; i++) { 1767 int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]); 1768 if (err) { 1769 return err; 1770 } 1771 } 1772 1773 // zero for reproducibility in case initial block is unreadable 1774 dir->rev = 0; 1775 1776 // rather than clobbering one of the blocks we just pretend 1777 // the revision may be valid 1778 int err = lfs_bd_read(lfs, 1779 NULL, &lfs->rcache, sizeof(dir->rev), 1780 dir->pair[0], 0, &dir->rev, sizeof(dir->rev)); 1781 dir->rev = lfs_fromle32(dir->rev); 1782 if (err && err != LFS_ERR_CORRUPT) { 1783 return err; 1784 } 1785 1786 // to make sure we don't immediately evict, align the new revision count 1787 // to our block_cycles modulus, see lfs_dir_compact for why our modulus 1788 // is tweaked this way 1789 if (lfs->cfg->block_cycles > 0) { 1790 dir->rev = lfs_alignup(dir->rev, ((lfs->cfg->block_cycles+1)|1)); 1791 } 1792 1793 // set defaults 1794 dir->off = sizeof(dir->rev); 1795 dir->etag = 0xffffffff; 1796 dir->count = 0; 1797 dir->tail[0] = LFS_BLOCK_NULL; 1798 dir->tail[1] = LFS_BLOCK_NULL; 1799 dir->erased = false; 1800 dir->split = false; 1801 1802 // don't write out yet, let caller take care of that 1803 return 0; 1804} 1805#endif 1806 1807#ifndef LFS_READONLY 1808static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { 1809 // steal state 1810 int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); 1811 if (err) { 1812 return err; 1813 } 1814 1815 // steal tail 1816 lfs_pair_tole32(tail->tail); 1817 err = lfs_dir_commit(lfs, dir, LFS_MKATTRS( 1818 {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail})); 1819 lfs_pair_fromle32(tail->tail); 1820 if (err) { 1821 return err; 1822 } 1823 1824 return 0; 1825} 1826#endif 1827 1828#ifndef LFS_READONLY 1829static int lfs_dir_split(lfs_t *lfs, 1830 lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, 1831 lfs_mdir_t *source, uint16_t split, uint16_t end) { 1832 // create tail metadata pair 1833 lfs_mdir_t tail; 1834 int err = lfs_dir_alloc(lfs, &tail); 1835 if (err) { 1836 return err; 1837 } 1838 1839 tail.split = dir->split; 1840 tail.tail[0] = dir->tail[0]; 1841 tail.tail[1] = dir->tail[1]; 1842 1843 // note we don't care about LFS_OK_RELOCATED 1844 int res = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end); 1845 if (res < 0) { 1846 return res; 1847 } 1848 1849 dir->tail[0] = tail.pair[0]; 1850 dir->tail[1] = tail.pair[1]; 1851 dir->split = true; 1852 1853 // update root if needed 1854 if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && split == 0) { 1855 lfs->root[0] = tail.pair[0]; 1856 lfs->root[1] = tail.pair[1]; 1857 } 1858 1859 return 0; 1860} 1861#endif 1862 1863#ifndef LFS_READONLY 1864static int lfs_dir_commit_size(void *p, lfs_tag_t tag, const void *buffer) { 1865 lfs_size_t *size = p; 1866 (void)buffer; 1867 1868 *size += lfs_tag_dsize(tag); 1869 return 0; 1870} 1871#endif 1872 1873#ifndef LFS_READONLY 1874struct lfs_dir_commit_commit { 1875 lfs_t *lfs; 1876 struct lfs_commit *commit; 1877}; 1878#endif 1879 1880#ifndef LFS_READONLY 1881static int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) { 1882 struct lfs_dir_commit_commit *commit = p; 1883 return lfs_dir_commitattr(commit->lfs, commit->commit, tag, buffer); 1884} 1885#endif 1886 1887#ifndef LFS_READONLY 1888static bool lfs_dir_needsrelocation(lfs_t *lfs, lfs_mdir_t *dir) { 1889 // If our revision count == n * block_cycles, we should force a relocation, 1890 // this is how littlefs wear-levels at the metadata-pair level. Note that we 1891 // actually use (block_cycles+1)|1, this is to avoid two corner cases: 1892 // 1. block_cycles = 1, which would prevent relocations from terminating 1893 // 2. block_cycles = 2n, which, due to aliasing, would only ever relocate 1894 // one metadata block in the pair, effectively making this useless 1895 return (lfs->cfg->block_cycles > 0 1896 && ((dir->rev + 1) % ((lfs->cfg->block_cycles+1)|1) == 0)); 1897} 1898#endif 1899 1900#ifndef LFS_READONLY 1901static int lfs_dir_compact(lfs_t *lfs, 1902 lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount, 1903 lfs_mdir_t *source, uint16_t begin, uint16_t end) { 1904 // save some state in case block is bad 1905 bool relocated = false; 1906 bool tired = lfs_dir_needsrelocation(lfs, dir); 1907 1908 // increment revision count 1909 dir->rev += 1; 1910 1911 // do not proactively relocate blocks during migrations, this 1912 // can cause a number of failure states such: clobbering the 1913 // v1 superblock if we relocate root, and invalidating directory 1914 // pointers if we relocate the head of a directory. On top of 1915 // this, relocations increase the overall complexity of 1916 // lfs_migration, which is already a delicate operation. 1917#ifdef LFS_MIGRATE 1918 if (lfs->lfs1) { 1919 tired = false; 1920 } 1921#endif 1922 1923 if (tired && lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) != 0) { 1924 // we're writing too much, time to relocate 1925 goto relocate; 1926 } 1927 1928 // begin loop to commit compaction to blocks until a compact sticks 1929 while (true) { 1930 { 1931 // setup commit state 1932 struct lfs_commit commit = { 1933 .block = dir->pair[1], 1934 .off = 0, 1935 .ptag = 0xffffffff, 1936 .crc = 0xffffffff, 1937 1938 .begin = 0, 1939 .end = (lfs->cfg->metadata_max ? 1940 lfs->cfg->metadata_max : lfs->cfg->block_size) - 8, 1941 }; 1942 1943 // erase block to write to 1944 int err = lfs_bd_erase(lfs, dir->pair[1]); 1945 if (err) { 1946 if (err == LFS_ERR_CORRUPT) { 1947 goto relocate; 1948 } 1949 return err; 1950 } 1951 1952 // write out header 1953 dir->rev = lfs_tole32(dir->rev); 1954 err = lfs_dir_commitprog(lfs, &commit, 1955 &dir->rev, sizeof(dir->rev)); 1956 dir->rev = lfs_fromle32(dir->rev); 1957 if (err) { 1958 if (err == LFS_ERR_CORRUPT) { 1959 goto relocate; 1960 } 1961 return err; 1962 } 1963 1964 // traverse the directory, this time writing out all unique tags 1965 err = lfs_dir_traverse(lfs, 1966 source, 0, 0xffffffff, attrs, attrcount, 1967 LFS_MKTAG(0x400, 0x3ff, 0), 1968 LFS_MKTAG(LFS_TYPE_NAME, 0, 0), 1969 begin, end, -begin, 1970 lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ 1971 lfs, &commit}); 1972 if (err) { 1973 if (err == LFS_ERR_CORRUPT) { 1974 goto relocate; 1975 } 1976 return err; 1977 } 1978 1979 // commit tail, which may be new after last size check 1980 if (!lfs_pair_isnull(dir->tail)) { 1981 lfs_pair_tole32(dir->tail); 1982 err = lfs_dir_commitattr(lfs, &commit, 1983 LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), 1984 dir->tail); 1985 lfs_pair_fromle32(dir->tail); 1986 if (err) { 1987 if (err == LFS_ERR_CORRUPT) { 1988 goto relocate; 1989 } 1990 return err; 1991 } 1992 } 1993 1994 // bring over gstate? 1995 lfs_gstate_t delta = {0}; 1996 if (!relocated) { 1997 lfs_gstate_xor(&delta, &lfs->gdisk); 1998 lfs_gstate_xor(&delta, &lfs->gstate); 1999 } 2000 lfs_gstate_xor(&delta, &lfs->gdelta); 2001 delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); 2002 2003 err = lfs_dir_getgstate(lfs, dir, &delta); 2004 if (err) { 2005 return err; 2006 } 2007 2008 if (!lfs_gstate_iszero(&delta)) { 2009 lfs_gstate_tole32(&delta); 2010 err = lfs_dir_commitattr(lfs, &commit, 2011 LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, 2012 sizeof(delta)), &delta); 2013 if (err) { 2014 if (err == LFS_ERR_CORRUPT) { 2015 goto relocate; 2016 } 2017 return err; 2018 } 2019 } 2020 2021 // complete commit with crc 2022 err = lfs_dir_commitcrc(lfs, &commit); 2023 if (err) { 2024 if (err == LFS_ERR_CORRUPT) { 2025 goto relocate; 2026 } 2027 return err; 2028 } 2029 2030 // successful compaction, swap dir pair to indicate most recent 2031 LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); 2032 lfs_pair_swap(dir->pair); 2033 dir->count = end - begin; 2034 dir->off = commit.off; 2035 dir->etag = commit.ptag; 2036 // update gstate 2037 lfs->gdelta = (lfs_gstate_t){0}; 2038 if (!relocated) { 2039 lfs->gdisk = lfs->gstate; 2040 } 2041 } 2042 break; 2043 2044relocate: 2045 // commit was corrupted, drop caches and prepare to relocate block 2046 relocated = true; 2047 lfs_cache_drop(lfs, &lfs->pcache); 2048 if (!tired) { 2049 LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]); 2050 } 2051 2052 // can't relocate superblock, filesystem is now frozen 2053 if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { 2054 LFS_WARN("Superblock 0x%"PRIx32" has become unwritable", 2055 dir->pair[1]); 2056 return LFS_ERR_NOSPC; 2057 } 2058 2059 // relocate half of pair 2060 int err = lfs_alloc(lfs, &dir->pair[1]); 2061 if (err && (err != LFS_ERR_NOSPC || !tired)) { 2062 return err; 2063 } 2064 2065 tired = false; 2066 continue; 2067 } 2068 2069 return relocated ? LFS_OK_RELOCATED : 0; 2070} 2071#endif 2072 2073#ifndef LFS_READONLY 2074static int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir, 2075 const struct lfs_mattr *attrs, int attrcount, 2076 lfs_mdir_t *source, uint16_t begin, uint16_t end) { 2077 while (true) { 2078 // find size of first split, we do this by halving the split until 2079 // the metadata is guaranteed to fit 2080 // 2081 // Note that this isn't a true binary search, we never increase the 2082 // split size. This may result in poorly distributed metadata but isn't 2083 // worth the extra code size or performance hit to fix. 2084 lfs_size_t split = begin; 2085 while (end - split > 1) { 2086 lfs_size_t size = 0; 2087 int err = lfs_dir_traverse(lfs, 2088 source, 0, 0xffffffff, attrs, attrcount, 2089 LFS_MKTAG(0x400, 0x3ff, 0), 2090 LFS_MKTAG(LFS_TYPE_NAME, 0, 0), 2091 split, end, -split, 2092 lfs_dir_commit_size, &size); 2093 if (err) { 2094 return err; 2095 } 2096 2097 // space is complicated, we need room for: 2098 // 2099 // - tail: 4+2*4 = 12 bytes 2100 // - gstate: 4+3*4 = 16 bytes 2101 // - move delete: 4 = 4 bytes 2102 // - crc: 4+4 = 8 bytes 2103 // total = 40 bytes 2104 // 2105 // And we cap at half a block to avoid degenerate cases with 2106 // nearly-full metadata blocks. 2107 // 2108 if (end - split < 0xff 2109 && size <= lfs_min( 2110 lfs->cfg->block_size - 40, 2111 lfs_alignup( 2112 (lfs->cfg->metadata_max 2113 ? lfs->cfg->metadata_max 2114 : lfs->cfg->block_size)/2, 2115 lfs->cfg->prog_size))) { 2116 break; 2117 } 2118 2119 split = split + ((end - split) / 2); 2120 } 2121 2122 if (split == begin) { 2123 // no split needed 2124 break; 2125 } 2126 2127 // split into two metadata pairs and continue 2128 int err = lfs_dir_split(lfs, dir, attrs, attrcount, 2129 source, split, end); 2130 if (err && err != LFS_ERR_NOSPC) { 2131 return err; 2132 } 2133 2134 if (err) { 2135 // we can't allocate a new block, try to compact with degraded 2136 // performance 2137 LFS_WARN("Unable to split {0x%"PRIx32", 0x%"PRIx32"}", 2138 dir->pair[0], dir->pair[1]); 2139 break; 2140 } else { 2141 end = split; 2142 } 2143 } 2144 2145 if (lfs_dir_needsrelocation(lfs, dir) 2146 && lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { 2147 // oh no! we're writing too much to the superblock, 2148 // should we expand? 2149 lfs_ssize_t size = lfs_fs_rawsize(lfs); 2150 if (size < 0) { 2151 return size; 2152 } 2153 2154 // do we have extra space? littlefs can't reclaim this space 2155 // by itself, so expand cautiously 2156 if ((lfs_size_t)size < lfs->block_count/2) { 2157 LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); 2158 int err = lfs_dir_split(lfs, dir, attrs, attrcount, 2159 source, begin, end); 2160 if (err && err != LFS_ERR_NOSPC) { 2161 return err; 2162 } 2163 2164 if (err) { 2165 // welp, we tried, if we ran out of space there's not much 2166 // we can do, we'll error later if we've become frozen 2167 LFS_WARN("Unable to expand superblock"); 2168 } else { 2169 end = begin; 2170 } 2171 } 2172 } 2173 2174 return lfs_dir_compact(lfs, dir, attrs, attrcount, source, begin, end); 2175} 2176#endif 2177 2178#ifndef LFS_READONLY 2179static int lfs_dir_relocatingcommit(lfs_t *lfs, lfs_mdir_t *dir, 2180 const lfs_block_t pair[2], 2181 const struct lfs_mattr *attrs, int attrcount, 2182 lfs_mdir_t *pdir) { 2183 int state = 0; 2184 2185 // calculate changes to the directory 2186 bool hasdelete = false; 2187 for (int i = 0; i < attrcount; i++) { 2188 if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { 2189 dir->count += 1; 2190 } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) { 2191 LFS_ASSERT(dir->count > 0); 2192 dir->count -= 1; 2193 hasdelete = true; 2194 } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) { 2195 dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0]; 2196 dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1]; 2197 dir->split = (lfs_tag_chunk(attrs[i].tag) & 1); 2198 lfs_pair_fromle32(dir->tail); 2199 } 2200 } 2201 2202 // should we actually drop the directory block? 2203 if (hasdelete && dir->count == 0) { 2204 LFS_ASSERT(pdir); 2205 int err = lfs_fs_pred(lfs, dir->pair, pdir); 2206 if (err && err != LFS_ERR_NOENT) { 2207 return err; 2208 } 2209 2210 if (err != LFS_ERR_NOENT && pdir->split) { 2211 state = LFS_OK_DROPPED; 2212 goto fixmlist; 2213 } 2214 } 2215 2216 if (dir->erased) { 2217 // try to commit 2218 struct lfs_commit commit = { 2219 .block = dir->pair[0], 2220 .off = dir->off, 2221 .ptag = dir->etag, 2222 .crc = 0xffffffff, 2223 2224 .begin = dir->off, 2225 .end = (lfs->cfg->metadata_max ? 2226 lfs->cfg->metadata_max : lfs->cfg->block_size) - 8, 2227 }; 2228 2229 // traverse attrs that need to be written out 2230 lfs_pair_tole32(dir->tail); 2231 int err = lfs_dir_traverse(lfs, 2232 dir, dir->off, dir->etag, attrs, attrcount, 2233 0, 0, 0, 0, 0, 2234 lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ 2235 lfs, &commit}); 2236 lfs_pair_fromle32(dir->tail); 2237 if (err) { 2238 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { 2239 goto compact; 2240 } 2241 return err; 2242 } 2243 2244 // commit any global diffs if we have any 2245 lfs_gstate_t delta = {0}; 2246 lfs_gstate_xor(&delta, &lfs->gstate); 2247 lfs_gstate_xor(&delta, &lfs->gdisk); 2248 lfs_gstate_xor(&delta, &lfs->gdelta); 2249 delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff); 2250 if (!lfs_gstate_iszero(&delta)) { 2251 err = lfs_dir_getgstate(lfs, dir, &delta); 2252 if (err) { 2253 return err; 2254 } 2255 2256 lfs_gstate_tole32(&delta); 2257 err = lfs_dir_commitattr(lfs, &commit, 2258 LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, 2259 sizeof(delta)), &delta); 2260 if (err) { 2261 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { 2262 goto compact; 2263 } 2264 return err; 2265 } 2266 } 2267 2268 // finalize commit with the crc 2269 err = lfs_dir_commitcrc(lfs, &commit); 2270 if (err) { 2271 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { 2272 goto compact; 2273 } 2274 return err; 2275 } 2276 2277 // successful commit, update dir 2278 LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); 2279 dir->off = commit.off; 2280 dir->etag = commit.ptag; 2281 // and update gstate 2282 lfs->gdisk = lfs->gstate; 2283 lfs->gdelta = (lfs_gstate_t){0}; 2284 2285 goto fixmlist; 2286 } 2287 2288compact: 2289 // fall back to compaction 2290 lfs_cache_drop(lfs, &lfs->pcache); 2291 2292 state = lfs_dir_splittingcompact(lfs, dir, attrs, attrcount, 2293 dir, 0, dir->count); 2294 if (state < 0) { 2295 return state; 2296 } 2297 2298 goto fixmlist; 2299 2300fixmlist:; 2301 // this complicated bit of logic is for fixing up any active 2302 // metadata-pairs that we may have affected 2303 // 2304 // note we have to make two passes since the mdir passed to 2305 // lfs_dir_commit could also be in this list, and even then 2306 // we need to copy the pair so they don't get clobbered if we refetch 2307 // our mdir. 2308 lfs_block_t oldpair[2] = {pair[0], pair[1]}; 2309 for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { 2310 if (lfs_pair_cmp(d->m.pair, oldpair) == 0) { 2311 d->m = *dir; 2312 if (d->m.pair != pair) { 2313 for (int i = 0; i < attrcount; i++) { 2314 if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && 2315 d->id == lfs_tag_id(attrs[i].tag)) { 2316 d->m.pair[0] = LFS_BLOCK_NULL; 2317 d->m.pair[1] = LFS_BLOCK_NULL; 2318 } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE && 2319 d->id > lfs_tag_id(attrs[i].tag)) { 2320 d->id -= 1; 2321 if (d->type == LFS_TYPE_DIR) { 2322 ((lfs_dir_t*)d)->pos -= 1; 2323 } 2324 } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE && 2325 d->id >= lfs_tag_id(attrs[i].tag)) { 2326 d->id += 1; 2327 if (d->type == LFS_TYPE_DIR) { 2328 ((lfs_dir_t*)d)->pos += 1; 2329 } 2330 } 2331 } 2332 } 2333 2334 while (d->id >= d->m.count && d->m.split) { 2335 // we split and id is on tail now 2336 d->id -= d->m.count; 2337 int err = lfs_dir_fetch(lfs, &d->m, d->m.tail); 2338 if (err) { 2339 return err; 2340 } 2341 } 2342 } 2343 } 2344 2345 return state; 2346} 2347#endif 2348 2349#ifndef LFS_READONLY 2350static int lfs_dir_orphaningcommit(lfs_t *lfs, lfs_mdir_t *dir, 2351 const struct lfs_mattr *attrs, int attrcount) { 2352 // check for any inline files that aren't RAM backed and 2353 // forcefully evict them, needed for filesystem consistency 2354 for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { 2355 if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 && 2356 f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) && 2357 f->ctz.size > lfs->cfg->cache_size) { 2358 int err = lfs_file_outline(lfs, f); 2359 if (err) { 2360 return err; 2361 } 2362 2363 err = lfs_file_flush(lfs, f); 2364 if (err) { 2365 return err; 2366 } 2367 } 2368 } 2369 2370 lfs_block_t lpair[2] = {dir->pair[0], dir->pair[1]}; 2371 lfs_mdir_t ldir = *dir; 2372 lfs_mdir_t pdir; 2373 int state = lfs_dir_relocatingcommit(lfs, &ldir, dir->pair, 2374 attrs, attrcount, &pdir); 2375 if (state < 0) { 2376 return state; 2377 } 2378 2379 // update if we're not in mlist, note we may have already been 2380 // updated if we are in mlist 2381 if (lfs_pair_cmp(dir->pair, lpair) == 0) { 2382 *dir = ldir; 2383 } 2384 2385 // commit was successful, but may require other changes in the 2386 // filesystem, these would normally be tail recursive, but we have 2387 // flattened them here avoid unbounded stack usage 2388 2389 // need to drop? 2390 if (state == LFS_OK_DROPPED) { 2391 // steal state 2392 int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); 2393 if (err) { 2394 return err; 2395 } 2396 2397 // steal tail, note that this can't create a recursive drop 2398 lpair[0] = pdir.pair[0]; 2399 lpair[1] = pdir.pair[1]; 2400 lfs_pair_tole32(dir->tail); 2401 state = lfs_dir_relocatingcommit(lfs, &pdir, lpair, LFS_MKATTRS( 2402 {LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), 2403 dir->tail}), 2404 NULL); 2405 lfs_pair_fromle32(dir->tail); 2406 if (state < 0) { 2407 return state; 2408 } 2409 2410 ldir = pdir; 2411 } 2412 2413 // need to relocate? 2414 bool orphans = false; 2415 while (state == LFS_OK_RELOCATED) { 2416 LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} " 2417 "-> {0x%"PRIx32", 0x%"PRIx32"}", 2418 lpair[0], lpair[1], ldir.pair[0], ldir.pair[1]); 2419 state = 0; 2420 2421 // update internal root 2422 if (lfs_pair_cmp(lpair, lfs->root) == 0) { 2423 lfs->root[0] = ldir.pair[0]; 2424 lfs->root[1] = ldir.pair[1]; 2425 } 2426 2427 // update internally tracked dirs 2428 for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) { 2429 if (lfs_pair_cmp(lpair, d->m.pair) == 0) { 2430 d->m.pair[0] = ldir.pair[0]; 2431 d->m.pair[1] = ldir.pair[1]; 2432 } 2433 2434 if (d->type == LFS_TYPE_DIR && 2435 lfs_pair_cmp(lpair, ((lfs_dir_t*)d)->head) == 0) { 2436 ((lfs_dir_t*)d)->head[0] = ldir.pair[0]; 2437 ((lfs_dir_t*)d)->head[1] = ldir.pair[1]; 2438 } 2439 } 2440 2441 // find parent 2442 lfs_stag_t tag = lfs_fs_parent(lfs, lpair, &pdir); 2443 if (tag < 0 && tag != LFS_ERR_NOENT) { 2444 return tag; 2445 } 2446 2447 bool hasparent = (tag != LFS_ERR_NOENT); 2448 if (tag != LFS_ERR_NOENT) { 2449 // note that if we have a parent, we must have a pred, so this will 2450 // always create an orphan 2451 int err = lfs_fs_preporphans(lfs, +1); 2452 if (err) { 2453 return err; 2454 } 2455 2456 // fix pending move in this pair? this looks like an optimization but 2457 // is in fact _required_ since relocating may outdate the move. 2458 uint16_t moveid = 0x3ff; 2459 if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) { 2460 moveid = lfs_tag_id(lfs->gstate.tag); 2461 LFS_DEBUG("Fixing move while relocating " 2462 "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", 2463 pdir.pair[0], pdir.pair[1], moveid); 2464 lfs_fs_prepmove(lfs, 0x3ff, NULL); 2465 if (moveid < lfs_tag_id(tag)) { 2466 tag -= LFS_MKTAG(0, 1, 0); 2467 } 2468 } 2469 2470 lfs_block_t ppair[2] = {pdir.pair[0], pdir.pair[1]}; 2471 lfs_pair_tole32(ldir.pair); 2472 state = lfs_dir_relocatingcommit(lfs, &pdir, ppair, LFS_MKATTRS( 2473 {LFS_MKTAG_IF(moveid != 0x3ff, 2474 LFS_TYPE_DELETE, moveid, 0), NULL}, 2475 {tag, ldir.pair}), 2476 NULL); 2477 lfs_pair_fromle32(ldir.pair); 2478 if (state < 0) { 2479 return state; 2480 } 2481 2482 if (state == LFS_OK_RELOCATED) { 2483 lpair[0] = ppair[0]; 2484 lpair[1] = ppair[1]; 2485 ldir = pdir; 2486 orphans = true; 2487 continue; 2488 } 2489 } 2490 2491 // find pred 2492 int err = lfs_fs_pred(lfs, lpair, &pdir); 2493 if (err && err != LFS_ERR_NOENT) { 2494 return err; 2495 } 2496 LFS_ASSERT(!(hasparent && err == LFS_ERR_NOENT)); 2497 2498 // if we can't find dir, it must be new 2499 if (err != LFS_ERR_NOENT) { 2500 if (lfs_gstate_hasorphans(&lfs->gstate)) { 2501 // next step, clean up orphans 2502 err = lfs_fs_preporphans(lfs, -hasparent); 2503 if (err) { 2504 return err; 2505 } 2506 } 2507 2508 // fix pending move in this pair? this looks like an optimization 2509 // but is in fact _required_ since relocating may outdate the move. 2510 uint16_t moveid = 0x3ff; 2511 if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) { 2512 moveid = lfs_tag_id(lfs->gstate.tag); 2513 LFS_DEBUG("Fixing move while relocating " 2514 "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", 2515 pdir.pair[0], pdir.pair[1], moveid); 2516 lfs_fs_prepmove(lfs, 0x3ff, NULL); 2517 } 2518 2519 // replace bad pair, either we clean up desync, or no desync occured 2520 lpair[0] = pdir.pair[0]; 2521 lpair[1] = pdir.pair[1]; 2522 lfs_pair_tole32(ldir.pair); 2523 state = lfs_dir_relocatingcommit(lfs, &pdir, lpair, LFS_MKATTRS( 2524 {LFS_MKTAG_IF(moveid != 0x3ff, 2525 LFS_TYPE_DELETE, moveid, 0), NULL}, 2526 {LFS_MKTAG(LFS_TYPE_TAIL + pdir.split, 0x3ff, 8), 2527 ldir.pair}), 2528 NULL); 2529 lfs_pair_fromle32(ldir.pair); 2530 if (state < 0) { 2531 return state; 2532 } 2533 2534 ldir = pdir; 2535 } 2536 } 2537 2538 return orphans ? LFS_OK_ORPHANED : 0; 2539} 2540#endif 2541 2542#ifndef LFS_READONLY 2543static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, 2544 const struct lfs_mattr *attrs, int attrcount) { 2545 int orphans = lfs_dir_orphaningcommit(lfs, dir, attrs, attrcount); 2546 if (orphans < 0) { 2547 return orphans; 2548 } 2549 2550 if (orphans) { 2551 // make sure we've removed all orphans, this is a noop if there 2552 // are none, but if we had nested blocks failures we may have 2553 // created some 2554 int err = lfs_fs_deorphan(lfs, false); 2555 if (err) { 2556 return err; 2557 } 2558 } 2559 2560 return 0; 2561} 2562#endif 2563 2564 2565/// Top level directory operations /// 2566#ifndef LFS_READONLY 2567static int lfs_rawmkdir(lfs_t *lfs, const char *path) { 2568 // deorphan if we haven't yet, needed at most once after poweron 2569 int err = lfs_fs_forceconsistency(lfs); 2570 if (err) { 2571 return err; 2572 } 2573 2574 struct lfs_mlist cwd; 2575 cwd.next = lfs->mlist; 2576 uint16_t id; 2577 err = lfs_dir_find(lfs, &cwd.m, &path, &id); 2578 if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { 2579 return (err < 0) ? err : LFS_ERR_EXIST; 2580 } 2581 2582 // check that name fits 2583 lfs_size_t nlen = strlen(path); 2584 if (nlen > lfs->name_max) { 2585 return LFS_ERR_NAMETOOLONG; 2586 } 2587 2588 // build up new directory 2589 lfs_alloc_ack(lfs); 2590 lfs_mdir_t dir; 2591 err = lfs_dir_alloc(lfs, &dir); 2592 if (err) { 2593 return err; 2594 } 2595 2596 // find end of list 2597 lfs_mdir_t pred = cwd.m; 2598 while (pred.split) { 2599 err = lfs_dir_fetch(lfs, &pred, pred.tail); 2600 if (err) { 2601 return err; 2602 } 2603 } 2604 2605 // setup dir 2606 lfs_pair_tole32(pred.tail); 2607 err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS( 2608 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); 2609 lfs_pair_fromle32(pred.tail); 2610 if (err) { 2611 return err; 2612 } 2613 2614 // current block not end of list? 2615 if (cwd.m.split) { 2616 // update tails, this creates a desync 2617 err = lfs_fs_preporphans(lfs, +1); 2618 if (err) { 2619 return err; 2620 } 2621 2622 // it's possible our predecessor has to be relocated, and if 2623 // our parent is our predecessor's predecessor, this could have 2624 // caused our parent to go out of date, fortunately we can hook 2625 // ourselves into littlefs to catch this 2626 cwd.type = 0; 2627 cwd.id = 0; 2628 lfs->mlist = &cwd; 2629 2630 lfs_pair_tole32(dir.pair); 2631 err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS( 2632 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); 2633 lfs_pair_fromle32(dir.pair); 2634 if (err) { 2635 lfs->mlist = cwd.next; 2636 return err; 2637 } 2638 2639 lfs->mlist = cwd.next; 2640 err = lfs_fs_preporphans(lfs, -1); 2641 if (err) { 2642 return err; 2643 } 2644 } 2645 2646 // now insert into our parent block 2647 lfs_pair_tole32(dir.pair); 2648 err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS( 2649 {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, 2650 {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path}, 2651 {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair}, 2652 {LFS_MKTAG_IF(!cwd.m.split, 2653 LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); 2654 lfs_pair_fromle32(dir.pair); 2655 if (err) { 2656 return err; 2657 } 2658 2659 return 0; 2660} 2661#endif 2662 2663static int lfs_dir_rawopen(lfs_t *lfs, lfs_dir_t *dir, const char *path) { 2664 lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL); 2665 if (tag < 0) { 2666 return tag; 2667 } 2668 2669 if (lfs_tag_type3(tag) != LFS_TYPE_DIR) { 2670 return LFS_ERR_NOTDIR; 2671 } 2672 2673 lfs_block_t pair[2]; 2674 if (lfs_tag_id(tag) == 0x3ff) { 2675 // handle root dir separately 2676 pair[0] = lfs->root[0]; 2677 pair[1] = lfs->root[1]; 2678 } else { 2679 // get dir pair from parent 2680 lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0), 2681 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); 2682 if (res < 0) { 2683 return res; 2684 } 2685 lfs_pair_fromle32(pair); 2686 } 2687 2688 // fetch first pair 2689 int err = lfs_dir_fetch(lfs, &dir->m, pair); 2690 if (err) { 2691 return err; 2692 } 2693 2694 // setup entry 2695 dir->head[0] = dir->m.pair[0]; 2696 dir->head[1] = dir->m.pair[1]; 2697 dir->id = 0; 2698 dir->pos = 0; 2699 2700 // add to list of mdirs 2701 dir->type = LFS_TYPE_DIR; 2702 lfs_mlist_append(lfs, (struct lfs_mlist *)dir); 2703 2704 return 0; 2705} 2706 2707static int lfs_dir_rawclose(lfs_t *lfs, lfs_dir_t *dir) { 2708 // remove from list of mdirs 2709 lfs_mlist_remove(lfs, (struct lfs_mlist *)dir); 2710 2711 return 0; 2712} 2713 2714static int lfs_dir_rawread(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { 2715 memset(info, 0, sizeof(*info)); 2716 2717 // special offset for '.' and '..' 2718 if (dir->pos == 0) { 2719 info->type = LFS_TYPE_DIR; 2720 strcpy(info->name, "."); 2721 dir->pos += 1; 2722 return true; 2723 } else if (dir->pos == 1) { 2724 info->type = LFS_TYPE_DIR; 2725 strcpy(info->name, ".."); 2726 dir->pos += 1; 2727 return true; 2728 } 2729 2730 while (true) { 2731 if (dir->id == dir->m.count) { 2732 if (!dir->m.split) { 2733 return false; 2734 } 2735 2736 int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); 2737 if (err) { 2738 return err; 2739 } 2740 2741 dir->id = 0; 2742 } 2743 2744 int err = lfs_dir_getinfo(lfs, &dir->m, dir->id, info); 2745 if (err && err != LFS_ERR_NOENT) { 2746 return err; 2747 } 2748 2749 dir->id += 1; 2750 if (err != LFS_ERR_NOENT) { 2751 break; 2752 } 2753 } 2754 2755 dir->pos += 1; 2756 return true; 2757} 2758 2759static int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { 2760 // simply walk from head dir 2761 int err = lfs_dir_rawrewind(lfs, dir); 2762 if (err) { 2763 return err; 2764 } 2765 2766 // first two for ./.. 2767 dir->pos = lfs_min(2, off); 2768 off -= dir->pos; 2769 2770 // skip superblock entry 2771 dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0); 2772 2773 while (off > 0) { 2774 if (dir->id == dir->m.count) { 2775 if (!dir->m.split) { 2776 return LFS_ERR_INVAL; 2777 } 2778 2779 err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail); 2780 if (err) { 2781 return err; 2782 } 2783 2784 dir->id = 0; 2785 } 2786 2787 int diff = lfs_min(dir->m.count - dir->id, off); 2788 dir->id += diff; 2789 dir->pos += diff; 2790 off -= diff; 2791 } 2792 2793 return 0; 2794} 2795 2796static lfs_soff_t lfs_dir_rawtell(lfs_t *lfs, lfs_dir_t *dir) { 2797 (void)lfs; 2798 return dir->pos; 2799} 2800 2801static int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir) { 2802 // reload the head dir 2803 int err = lfs_dir_fetch(lfs, &dir->m, dir->head); 2804 if (err) { 2805 return err; 2806 } 2807 2808 dir->id = 0; 2809 dir->pos = 0; 2810 return 0; 2811} 2812 2813 2814/// File index list operations /// 2815static int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) { 2816 lfs_off_t size = *off; 2817 lfs_off_t b = lfs->cfg->block_size - 2*4; 2818 lfs_off_t i = size / b; 2819 if (i == 0) { 2820 return 0; 2821 } 2822 2823 i = (size - 4*(lfs_popc(i-1)+2)) / b; 2824 *off = size - b*i - 4*lfs_popc(i); 2825 return i; 2826} 2827 2828static int lfs_ctz_find(lfs_t *lfs, 2829 const lfs_cache_t *pcache, lfs_cache_t *rcache, 2830 lfs_block_t head, lfs_size_t size, 2831 lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) { 2832 if (size == 0) { 2833 *block = LFS_BLOCK_NULL; 2834 *off = 0; 2835 return 0; 2836 } 2837 2838 lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); 2839 lfs_off_t target = lfs_ctz_index(lfs, &pos); 2840 2841 while (current > target) { 2842 lfs_size_t skip = lfs_min( 2843 lfs_npw2(current-target+1) - 1, 2844 lfs_ctz(current)); 2845 2846 int err = lfs_bd_read(lfs, 2847 pcache, rcache, sizeof(head), 2848 head, 4*skip, &head, sizeof(head)); 2849 head = lfs_fromle32(head); 2850 if (err) { 2851 return err; 2852 } 2853 2854 current -= 1 << skip; 2855 } 2856 2857 *block = head; 2858 *off = pos; 2859 return 0; 2860} 2861 2862#ifndef LFS_READONLY 2863static int lfs_ctz_extend(lfs_t *lfs, 2864 lfs_cache_t *pcache, lfs_cache_t *rcache, 2865 lfs_block_t head, lfs_size_t size, 2866 lfs_block_t *block, lfs_off_t *off) { 2867 while (true) { 2868 // go ahead and grab a block 2869 lfs_block_t nblock; 2870 int err = lfs_alloc(lfs, &nblock); 2871 if (err) { 2872 return err; 2873 } 2874 2875 { 2876 err = lfs_bd_erase(lfs, nblock); 2877 if (err) { 2878 if (err == LFS_ERR_CORRUPT) { 2879 goto relocate; 2880 } 2881 return err; 2882 } 2883 2884 if (size == 0) { 2885 *block = nblock; 2886 *off = 0; 2887 return 0; 2888 } 2889 2890 lfs_size_t noff = size - 1; 2891 lfs_off_t index = lfs_ctz_index(lfs, &noff); 2892 noff = noff + 1; 2893 2894 // just copy out the last block if it is incomplete 2895 if (noff != lfs->cfg->block_size) { 2896 for (lfs_off_t i = 0; i < noff; i++) { 2897 uint8_t data; 2898 err = lfs_bd_read(lfs, 2899 NULL, rcache, noff-i, 2900 head, i, &data, 1); 2901 if (err) { 2902 return err; 2903 } 2904 2905 err = lfs_bd_prog(lfs, 2906 pcache, rcache, true, 2907 nblock, i, &data, 1); 2908 if (err) { 2909 if (err == LFS_ERR_CORRUPT) { 2910 goto relocate; 2911 } 2912 return err; 2913 } 2914 } 2915 2916 *block = nblock; 2917 *off = noff; 2918 return 0; 2919 } 2920 2921 // append block 2922 index += 1; 2923 lfs_size_t skips = lfs_ctz(index) + 1; 2924 lfs_block_t nhead = head; 2925 for (lfs_off_t i = 0; i < skips; i++) { 2926 nhead = lfs_tole32(nhead); 2927 err = lfs_bd_prog(lfs, pcache, rcache, true, 2928 nblock, 4*i, &nhead, 4); 2929 nhead = lfs_fromle32(nhead); 2930 if (err) { 2931 if (err == LFS_ERR_CORRUPT) { 2932 goto relocate; 2933 } 2934 return err; 2935 } 2936 2937 if (i != skips-1) { 2938 err = lfs_bd_read(lfs, 2939 NULL, rcache, sizeof(nhead), 2940 nhead, 4*i, &nhead, sizeof(nhead)); 2941 nhead = lfs_fromle32(nhead); 2942 if (err) { 2943 return err; 2944 } 2945 } 2946 } 2947 2948 *block = nblock; 2949 *off = 4*skips; 2950 return 0; 2951 } 2952 2953relocate: 2954 LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); 2955 2956 // just clear cache and try a new block 2957 lfs_cache_drop(lfs, pcache); 2958 } 2959} 2960#endif 2961 2962static int lfs_ctz_traverse(lfs_t *lfs, 2963 const lfs_cache_t *pcache, lfs_cache_t *rcache, 2964 lfs_block_t head, lfs_size_t size, 2965 int (*cb)(void*, lfs_block_t), void *data) { 2966 if (size == 0) { 2967 return 0; 2968 } 2969 2970 lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1}); 2971 2972 while (true) { 2973 int err = cb(data, head); 2974 if (err) { 2975 return err; 2976 } 2977 2978 if (index == 0) { 2979 return 0; 2980 } 2981 2982 lfs_block_t heads[2]; 2983 int count = 2 - (index & 1); 2984 err = lfs_bd_read(lfs, 2985 pcache, rcache, count*sizeof(head), 2986 head, 0, &heads, count*sizeof(head)); 2987 heads[0] = lfs_fromle32(heads[0]); 2988 heads[1] = lfs_fromle32(heads[1]); 2989 if (err) { 2990 return err; 2991 } 2992 2993 for (int i = 0; i < count-1; i++) { 2994 err = cb(data, heads[i]); 2995 if (err) { 2996 return err; 2997 } 2998 } 2999 3000 head = heads[count-1]; 3001 index -= count; 3002 } 3003} 3004 3005 3006/// Top level file operations /// 3007static int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file, 3008 const char *path, int flags, 3009 const struct lfs_file_config *cfg) { 3010#ifndef LFS_READONLY 3011 // deorphan if we haven't yet, needed at most once after poweron 3012 if ((flags & LFS_O_WRONLY) == LFS_O_WRONLY) { 3013 int err = lfs_fs_forceconsistency(lfs); 3014 if (err) { 3015 return err; 3016 } 3017 } 3018#else 3019 LFS_ASSERT((flags & LFS_O_RDONLY) == LFS_O_RDONLY); 3020#endif 3021 3022 // setup simple file details 3023 int err; 3024 file->cfg = cfg; 3025 file->flags = flags; 3026 file->pos = 0; 3027 file->off = 0; 3028 file->cache.buffer = NULL; 3029 3030 // allocate entry for file if it doesn't exist 3031 lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id); 3032 if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) { 3033 err = tag; 3034 goto cleanup; 3035 } 3036 3037 // get id, add to list of mdirs to catch update changes 3038 file->type = LFS_TYPE_REG; 3039 lfs_mlist_append(lfs, (struct lfs_mlist *)file); 3040 3041#ifdef LFS_READONLY 3042 if (tag == LFS_ERR_NOENT) { 3043 err = LFS_ERR_NOENT; 3044 goto cleanup; 3045#else 3046 if (tag == LFS_ERR_NOENT) { 3047 if (!(flags & LFS_O_CREAT)) { 3048 err = LFS_ERR_NOENT; 3049 goto cleanup; 3050 } 3051 3052 // check that name fits 3053 lfs_size_t nlen = strlen(path); 3054 if (nlen > lfs->name_max) { 3055 err = LFS_ERR_NAMETOOLONG; 3056 goto cleanup; 3057 } 3058 3059 // get next slot and create entry to remember name 3060 err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( 3061 {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL}, 3062 {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path}, 3063 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL})); 3064 3065 // it may happen that the file name doesn't fit in the metadata blocks, e.g., a 256 byte file name will 3066 // not fit in a 128 byte block. 3067 err = (err == LFS_ERR_NOSPC) ? LFS_ERR_NAMETOOLONG : err; 3068 if (err) { 3069 goto cleanup; 3070 } 3071 3072 tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0); 3073 } else if (flags & LFS_O_EXCL) { 3074 err = LFS_ERR_EXIST; 3075 goto cleanup; 3076#endif 3077 } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) { 3078 err = LFS_ERR_ISDIR; 3079 goto cleanup; 3080#ifndef LFS_READONLY 3081 } else if (flags & LFS_O_TRUNC) { 3082 // truncate if requested 3083 tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0); 3084 file->flags |= LFS_F_DIRTY; 3085#endif 3086 } else { 3087 // try to load what's on disk, if it's inlined we'll fix it later 3088 tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0), 3089 LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz); 3090 if (tag < 0) { 3091 err = tag; 3092 goto cleanup; 3093 } 3094 lfs_ctz_fromle32(&file->ctz); 3095 } 3096 3097 // fetch attrs 3098 for (unsigned i = 0; i < file->cfg->attr_count; i++) { 3099 // if opened for read / read-write operations 3100 if ((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY) { 3101 lfs_stag_t res = lfs_dir_get(lfs, &file->m, 3102 LFS_MKTAG(0x7ff, 0x3ff, 0), 3103 LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type, 3104 file->id, file->cfg->attrs[i].size), 3105 file->cfg->attrs[i].buffer); 3106 if (res < 0 && res != LFS_ERR_NOENT) { 3107 err = res; 3108 goto cleanup; 3109 } 3110 } 3111 3112#ifndef LFS_READONLY 3113 // if opened for write / read-write operations 3114 if ((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY) { 3115 if (file->cfg->attrs[i].size > lfs->attr_max) { 3116 err = LFS_ERR_NOSPC; 3117 goto cleanup; 3118 } 3119 3120 file->flags |= LFS_F_DIRTY; 3121 } 3122#endif 3123 } 3124 3125 // allocate buffer if needed 3126 if (file->cfg->buffer) { 3127 file->cache.buffer = file->cfg->buffer; 3128 } else { 3129 file->cache.buffer = lfs_malloc(lfs->cfg->cache_size); 3130 if (!file->cache.buffer) { 3131 err = LFS_ERR_NOMEM; 3132 goto cleanup; 3133 } 3134 } 3135 3136 // zero to avoid information leak 3137 lfs_cache_zero(lfs, &file->cache); 3138 3139 if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) { 3140 // load inline files 3141 file->ctz.head = LFS_BLOCK_INLINE; 3142 file->ctz.size = lfs_tag_size(tag); 3143 file->flags |= LFS_F_INLINE; 3144 file->cache.block = file->ctz.head; 3145 file->cache.off = 0; 3146 file->cache.size = lfs->cfg->cache_size; 3147 3148 // don't always read (may be new/trunc file) 3149 if (file->ctz.size > 0) { 3150 lfs_stag_t res = lfs_dir_get(lfs, &file->m, 3151 LFS_MKTAG(0x700, 0x3ff, 0), 3152 LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 3153 lfs_min(file->cache.size, 0x3fe)), 3154 file->cache.buffer); 3155 if (res < 0) { 3156 err = res; 3157 goto cleanup; 3158 } 3159 } 3160 } 3161 3162 return 0; 3163 3164cleanup: 3165 // clean up lingering resources 3166#ifndef LFS_READONLY 3167 file->flags |= LFS_F_ERRED; 3168#endif 3169 lfs_file_rawclose(lfs, file); 3170 return err; 3171} 3172 3173#ifndef LFS_NO_MALLOC 3174static int lfs_file_rawopen(lfs_t *lfs, lfs_file_t *file, 3175 const char *path, int flags) { 3176 static const struct lfs_file_config defaults = {0}; 3177 int err = lfs_file_rawopencfg(lfs, file, path, flags, &defaults); 3178 return err; 3179} 3180#endif 3181 3182static int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file) { 3183#ifndef LFS_READONLY 3184 int err = lfs_file_rawsync(lfs, file); 3185#else 3186 int err = 0; 3187#endif 3188 3189 // remove from list of mdirs 3190 lfs_mlist_remove(lfs, (struct lfs_mlist*)file); 3191 3192 // clean up memory 3193 if (!file->cfg->buffer) { 3194 lfs_free(file->cache.buffer); 3195 } 3196 3197 return err; 3198} 3199 3200 3201#ifndef LFS_READONLY 3202static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { 3203 while (true) { 3204 // just relocate what exists into new block 3205 lfs_block_t nblock; 3206 int err = lfs_alloc(lfs, &nblock); 3207 if (err) { 3208 return err; 3209 } 3210 3211 err = lfs_bd_erase(lfs, nblock); 3212 if (err) { 3213 if (err == LFS_ERR_CORRUPT) { 3214 goto relocate; 3215 } 3216 return err; 3217 } 3218 3219 // either read from dirty cache or disk 3220 for (lfs_off_t i = 0; i < file->off; i++) { 3221 uint8_t data; 3222 if (file->flags & LFS_F_INLINE) { 3223 err = lfs_dir_getread(lfs, &file->m, 3224 // note we evict inline files before they can be dirty 3225 NULL, &file->cache, file->off-i, 3226 LFS_MKTAG(0xfff, 0x1ff, 0), 3227 LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), 3228 i, &data, 1); 3229 if (err) { 3230 return err; 3231 } 3232 } else { 3233 err = lfs_bd_read(lfs, 3234 &file->cache, &lfs->rcache, file->off-i, 3235 file->block, i, &data, 1); 3236 if (err) { 3237 return err; 3238 } 3239 } 3240 3241 err = lfs_bd_prog(lfs, 3242 &lfs->pcache, &lfs->rcache, true, 3243 nblock, i, &data, 1); 3244 if (err) { 3245 if (err == LFS_ERR_CORRUPT) { 3246 goto relocate; 3247 } 3248 return err; 3249 } 3250 } 3251 3252 // copy over new state of file 3253 memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->cache_size); 3254 file->cache.block = lfs->pcache.block; 3255 file->cache.off = lfs->pcache.off; 3256 file->cache.size = lfs->pcache.size; 3257 lfs_cache_zero(lfs, &lfs->pcache); 3258 3259 file->block = nblock; 3260 file->flags |= LFS_F_WRITING; 3261 return 0; 3262 3263relocate: 3264 LFS_DEBUG("Bad block at 0x%"PRIx32, nblock); 3265 3266 // just clear cache and try a new block 3267 lfs_cache_drop(lfs, &lfs->pcache); 3268 } 3269} 3270#endif 3271 3272#ifndef LFS_READONLY 3273static int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) { 3274 file->off = file->pos; 3275 lfs_alloc_ack(lfs); 3276 int err = lfs_file_relocate(lfs, file); 3277 if (err) { 3278 return err; 3279 } 3280 3281 file->flags &= ~LFS_F_INLINE; 3282 return 0; 3283} 3284#endif 3285 3286static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { 3287 if (file->flags & LFS_F_READING) { 3288 if (!(file->flags & LFS_F_INLINE)) { 3289 lfs_cache_drop(lfs, &file->cache); 3290 } 3291 file->flags &= ~LFS_F_READING; 3292 } 3293 3294#ifndef LFS_READONLY 3295 if (file->flags & LFS_F_WRITING) { 3296 lfs_off_t pos = file->pos; 3297 3298 if (!(file->flags & LFS_F_INLINE)) { 3299 // copy over anything after current branch 3300 lfs_file_t orig = { 3301 .ctz.head = file->ctz.head, 3302 .ctz.size = file->ctz.size, 3303 .flags = LFS_O_RDONLY, 3304 .pos = file->pos, 3305 .cache = lfs->rcache, 3306 }; 3307 lfs_cache_drop(lfs, &lfs->rcache); 3308 3309 while (file->pos < file->ctz.size) { 3310 // copy over a byte at a time, leave it up to caching 3311 // to make this efficient 3312 uint8_t data; 3313 lfs_ssize_t res = lfs_file_flushedread(lfs, &orig, &data, 1); 3314 if (res < 0) { 3315 return res; 3316 } 3317 3318 res = lfs_file_flushedwrite(lfs, file, &data, 1); 3319 if (res < 0) { 3320 return res; 3321 } 3322 3323 // keep our reference to the rcache in sync 3324 if (lfs->rcache.block != LFS_BLOCK_NULL) { 3325 lfs_cache_drop(lfs, &orig.cache); 3326 lfs_cache_drop(lfs, &lfs->rcache); 3327 } 3328 } 3329 3330 // write out what we have 3331 while (true) { 3332 int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true); 3333 if (err) { 3334 if (err == LFS_ERR_CORRUPT) { 3335 goto relocate; 3336 } 3337 return err; 3338 } 3339 3340 break; 3341 3342relocate: 3343 LFS_DEBUG("Bad block at 0x%"PRIx32, file->block); 3344 err = lfs_file_relocate(lfs, file); 3345 if (err) { 3346 return err; 3347 } 3348 } 3349 } else { 3350 file->pos = lfs_max(file->pos, file->ctz.size); 3351 } 3352 3353 // actual file updates 3354 file->ctz.head = file->block; 3355 file->ctz.size = file->pos; 3356 file->flags &= ~LFS_F_WRITING; 3357 file->flags |= LFS_F_DIRTY; 3358 3359 file->pos = pos; 3360 } 3361#endif 3362 3363 return 0; 3364} 3365 3366#ifndef LFS_READONLY 3367static int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) { 3368 if (file->flags & LFS_F_ERRED) { 3369 // it's not safe to do anything if our file errored 3370 return 0; 3371 } 3372 3373 int err = lfs_file_flush(lfs, file); 3374 if (err) { 3375 file->flags |= LFS_F_ERRED; 3376 return err; 3377 } 3378 3379 3380 if ((file->flags & LFS_F_DIRTY) && 3381 !lfs_pair_isnull(file->m.pair)) { 3382 // update dir entry 3383 uint16_t type; 3384 const void *buffer; 3385 lfs_size_t size; 3386 struct lfs_ctz ctz; 3387 if (file->flags & LFS_F_INLINE) { 3388 // inline the whole file 3389 type = LFS_TYPE_INLINESTRUCT; 3390 buffer = file->cache.buffer; 3391 size = file->ctz.size; 3392 } else { 3393 // update the ctz reference 3394 type = LFS_TYPE_CTZSTRUCT; 3395 // copy ctz so alloc will work during a relocate 3396 ctz = file->ctz; 3397 lfs_ctz_tole32(&ctz); 3398 buffer = &ctz; 3399 size = sizeof(ctz); 3400 } 3401 3402 // commit file data and attributes 3403 err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS( 3404 {LFS_MKTAG(type, file->id, size), buffer}, 3405 {LFS_MKTAG(LFS_FROM_USERATTRS, file->id, 3406 file->cfg->attr_count), file->cfg->attrs})); 3407 if (err) { 3408 file->flags |= LFS_F_ERRED; 3409 return err; 3410 } 3411 3412 file->flags &= ~LFS_F_DIRTY; 3413 } 3414 3415 return 0; 3416} 3417#endif 3418 3419static lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file, 3420 void *buffer, lfs_size_t size) { 3421 uint8_t *data = buffer; 3422 lfs_size_t nsize = size; 3423 3424 if (file->pos >= file->ctz.size) { 3425 // eof if past end 3426 return 0; 3427 } 3428 3429 size = lfs_min(size, file->ctz.size - file->pos); 3430 nsize = size; 3431 3432 while (nsize > 0) { 3433 // check if we need a new block 3434 if (!(file->flags & LFS_F_READING) || 3435 file->off == lfs->cfg->block_size) { 3436 if (!(file->flags & LFS_F_INLINE)) { 3437 int err = lfs_ctz_find(lfs, NULL, &file->cache, 3438 file->ctz.head, file->ctz.size, 3439 file->pos, &file->block, &file->off); 3440 if (err) { 3441 return err; 3442 } 3443 } else { 3444 file->block = LFS_BLOCK_INLINE; 3445 file->off = file->pos; 3446 } 3447 3448 file->flags |= LFS_F_READING; 3449 } 3450 3451 // read as much as we can in current block 3452 lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); 3453 if (file->flags & LFS_F_INLINE) { 3454 int err = lfs_dir_getread(lfs, &file->m, 3455 NULL, &file->cache, lfs->cfg->block_size, 3456 LFS_MKTAG(0xfff, 0x1ff, 0), 3457 LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), 3458 file->off, data, diff); 3459 if (err) { 3460 return err; 3461 } 3462 } else { 3463 int err = lfs_bd_read(lfs, 3464 NULL, &file->cache, lfs->cfg->block_size, 3465 file->block, file->off, data, diff); 3466 if (err) { 3467 return err; 3468 } 3469 } 3470 3471 file->pos += diff; 3472 file->off += diff; 3473 data += diff; 3474 nsize -= diff; 3475 } 3476 3477 return size; 3478} 3479 3480static lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file, 3481 void *buffer, lfs_size_t size) { 3482 LFS_ASSERT((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY); 3483 3484#ifndef LFS_READONLY 3485 if (file->flags & LFS_F_WRITING) { 3486 // flush out any writes 3487 int err = lfs_file_flush(lfs, file); 3488 if (err) { 3489 return err; 3490 } 3491 } 3492#endif 3493 3494 return lfs_file_flushedread(lfs, file, buffer, size); 3495} 3496 3497 3498#ifndef LFS_READONLY 3499static lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file, 3500 const void *buffer, lfs_size_t size) { 3501 const uint8_t *data = buffer; 3502 lfs_size_t nsize = size; 3503 3504 if ((file->flags & LFS_F_INLINE) && 3505 lfs_max(file->pos+nsize, file->ctz.size) > 3506 lfs_min(0x3fe, lfs_min( 3507 lfs->cfg->cache_size, 3508 (lfs->cfg->metadata_max ? 3509 lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) { 3510 // inline file doesn't fit anymore 3511 int err = lfs_file_outline(lfs, file); 3512 if (err) { 3513 file->flags |= LFS_F_ERRED; 3514 return err; 3515 } 3516 } 3517 3518 while (nsize > 0) { 3519 // check if we need a new block 3520 if (!(file->flags & LFS_F_WRITING) || 3521 file->off == lfs->cfg->block_size) { 3522 if (!(file->flags & LFS_F_INLINE)) { 3523 if (!(file->flags & LFS_F_WRITING) && file->pos > 0) { 3524 // find out which block we're extending from 3525 int err = lfs_ctz_find(lfs, NULL, &file->cache, 3526 file->ctz.head, file->ctz.size, 3527 file->pos-1, &file->block, &(lfs_off_t){0}); 3528 if (err) { 3529 file->flags |= LFS_F_ERRED; 3530 return err; 3531 } 3532 3533 // mark cache as dirty since we may have read data into it 3534 lfs_cache_zero(lfs, &file->cache); 3535 } 3536 3537 // extend file with new blocks 3538 lfs_alloc_ack(lfs); 3539 int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache, 3540 file->block, file->pos, 3541 &file->block, &file->off); 3542 if (err) { 3543 file->flags |= LFS_F_ERRED; 3544 return err; 3545 } 3546 } else { 3547 file->block = LFS_BLOCK_INLINE; 3548 file->off = file->pos; 3549 } 3550 3551 file->flags |= LFS_F_WRITING; 3552 } 3553 3554 // program as much as we can in current block 3555 lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off); 3556 while (true) { 3557 int err = lfs_bd_prog(lfs, &file->cache, &lfs->rcache, true, 3558 file->block, file->off, data, diff); 3559 if (err) { 3560 if (err == LFS_ERR_CORRUPT) { 3561 goto relocate; 3562 } 3563 file->flags |= LFS_F_ERRED; 3564 return err; 3565 } 3566 3567 break; 3568relocate: 3569 err = lfs_file_relocate(lfs, file); 3570 if (err) { 3571 file->flags |= LFS_F_ERRED; 3572 return err; 3573 } 3574 } 3575 3576 file->pos += diff; 3577 file->off += diff; 3578 data += diff; 3579 nsize -= diff; 3580 3581 lfs_alloc_ack(lfs); 3582 } 3583 3584 return size; 3585} 3586 3587static lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file, 3588 const void *buffer, lfs_size_t size) { 3589 LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY); 3590 3591 if (file->flags & LFS_F_READING) { 3592 // drop any reads 3593 int err = lfs_file_flush(lfs, file); 3594 if (err) { 3595 return err; 3596 } 3597 } 3598 3599 if ((file->flags & LFS_O_APPEND) && file->pos < file->ctz.size) { 3600 file->pos = file->ctz.size; 3601 } 3602 3603 if (file->pos + size > lfs->file_max) { 3604 // Larger than file limit? 3605 return LFS_ERR_FBIG; 3606 } 3607 3608 if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) { 3609 // fill with zeros 3610 lfs_off_t pos = file->pos; 3611 file->pos = file->ctz.size; 3612 3613 while (file->pos < pos) { 3614 lfs_ssize_t res = lfs_file_flushedwrite(lfs, file, &(uint8_t){0}, 1); 3615 if (res < 0) { 3616 return res; 3617 } 3618 } 3619 } 3620 3621 lfs_ssize_t nsize = lfs_file_flushedwrite(lfs, file, buffer, size); 3622 if (nsize < 0) { 3623 return nsize; 3624 } 3625 3626 file->flags &= ~LFS_F_ERRED; 3627 return nsize; 3628} 3629#endif 3630 3631static lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file, 3632 lfs_soff_t off, int whence) { 3633 // find new pos 3634 lfs_off_t npos = file->pos; 3635 if (whence == LFS_SEEK_SET) { 3636 npos = off; 3637 } else if (whence == LFS_SEEK_CUR) { 3638 if ((lfs_soff_t)file->pos + off < 0) { 3639 return LFS_ERR_INVAL; 3640 } else { 3641 npos = file->pos + off; 3642 } 3643 } else if (whence == LFS_SEEK_END) { 3644 lfs_soff_t res = lfs_file_rawsize(lfs, file) + off; 3645 if (res < 0) { 3646 return LFS_ERR_INVAL; 3647 } else { 3648 npos = res; 3649 } 3650 } 3651 3652 if (npos > lfs->file_max) { 3653 // file position out of range 3654 return LFS_ERR_INVAL; 3655 } 3656 3657 if (file->pos == npos) { 3658 // noop - position has not changed 3659 return npos; 3660 } 3661 3662 // if we're only reading and our new offset is still in the file's cache 3663 // we can avoid flushing and needing to reread the data 3664 if ( 3665#ifndef LFS_READONLY 3666 !(file->flags & LFS_F_WRITING) 3667#else 3668 true 3669#endif 3670 ) { 3671 int oindex = lfs_ctz_index(lfs, &(lfs_off_t){file->pos}); 3672 lfs_off_t noff = npos; 3673 int nindex = lfs_ctz_index(lfs, &noff); 3674 if (oindex == nindex 3675 && noff >= file->cache.off 3676 && noff < file->cache.off + file->cache.size) { 3677 file->pos = npos; 3678 file->off = noff; 3679 return npos; 3680 } 3681 } 3682 3683 // write out everything beforehand, may be noop if rdonly 3684 int err = lfs_file_flush(lfs, file); 3685 if (err) { 3686 return err; 3687 } 3688 3689 // update pos 3690 file->pos = npos; 3691 return npos; 3692} 3693 3694#ifndef LFS_READONLY 3695static int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { 3696 LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY); 3697 3698 if (size > LFS_FILE_MAX) { 3699 return LFS_ERR_INVAL; 3700 } 3701 3702 lfs_off_t pos = file->pos; 3703 lfs_off_t oldsize = lfs_file_rawsize(lfs, file); 3704 if (size < oldsize) { 3705 // revert to inline file? 3706 if (size <= lfs_min(0x3fe, lfs_min( 3707 lfs->cfg->cache_size, 3708 (lfs->cfg->metadata_max ? 3709 lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) { 3710 // flush+seek to head 3711 lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET); 3712 if (res < 0) { 3713 return (int)res; 3714 } 3715 3716 // read our data into rcache temporarily 3717 lfs_cache_drop(lfs, &lfs->rcache); 3718 res = lfs_file_flushedread(lfs, file, 3719 lfs->rcache.buffer, size); 3720 if (res < 0) { 3721 return (int)res; 3722 } 3723 3724 file->ctz.head = LFS_BLOCK_INLINE; 3725 file->ctz.size = size; 3726 file->flags |= LFS_F_DIRTY | LFS_F_READING | LFS_F_INLINE; 3727 file->cache.block = file->ctz.head; 3728 file->cache.off = 0; 3729 file->cache.size = lfs->cfg->cache_size; 3730 memcpy(file->cache.buffer, lfs->rcache.buffer, size); 3731 3732 } else { 3733 // need to flush since directly changing metadata 3734 int err = lfs_file_flush(lfs, file); 3735 if (err) { 3736 return err; 3737 } 3738 3739 // lookup new head in ctz skip list 3740 err = lfs_ctz_find(lfs, NULL, &file->cache, 3741 file->ctz.head, file->ctz.size, 3742 size-1, &file->block, &(lfs_off_t){0}); 3743 if (err) { 3744 return err; 3745 } 3746 3747 // need to set pos/block/off consistently so seeking back to 3748 // the old position does not get confused 3749 file->pos = size; 3750 file->ctz.head = file->block; 3751 file->ctz.size = size; 3752 file->flags |= LFS_F_DIRTY | LFS_F_READING; 3753 } 3754 } else if (size > oldsize) { 3755 // flush+seek if not already at end 3756 lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END); 3757 if (res < 0) { 3758 return (int)res; 3759 } 3760 3761 // fill with zeros 3762 while (file->pos < size) { 3763 res = lfs_file_rawwrite(lfs, file, &(uint8_t){0}, 1); 3764 if (res < 0) { 3765 return (int)res; 3766 } 3767 } 3768 } 3769 3770 // restore pos 3771 lfs_soff_t res = lfs_file_rawseek(lfs, file, pos, LFS_SEEK_SET); 3772 if (res < 0) { 3773 return (int)res; 3774 } 3775 3776 return 0; 3777} 3778#endif 3779 3780static lfs_soff_t lfs_file_rawtell(lfs_t *lfs, lfs_file_t *file) { 3781 (void)lfs; 3782 return file->pos; 3783} 3784 3785static int lfs_file_rawrewind(lfs_t *lfs, lfs_file_t *file) { 3786 lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET); 3787 if (res < 0) { 3788 return (int)res; 3789 } 3790 3791 return 0; 3792} 3793 3794static lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file) { 3795 (void)lfs; 3796 3797#ifndef LFS_READONLY 3798 if (file->flags & LFS_F_WRITING) { 3799 return lfs_max(file->pos, file->ctz.size); 3800 } 3801#endif 3802 3803 return file->ctz.size; 3804} 3805 3806 3807/// General fs operations /// 3808static int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) { 3809 lfs_mdir_t cwd; 3810 lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); 3811 if (tag < 0) { 3812 return (int)tag; 3813 } 3814 3815 return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info); 3816} 3817 3818#ifndef LFS_READONLY 3819static int lfs_rawremove(lfs_t *lfs, const char *path) { 3820 // deorphan if we haven't yet, needed at most once after poweron 3821 int err = lfs_fs_forceconsistency(lfs); 3822 if (err) { 3823 return err; 3824 } 3825 3826 lfs_mdir_t cwd; 3827 lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); 3828 if (tag < 0 || lfs_tag_id(tag) == 0x3ff) { 3829 return (tag < 0) ? (int)tag : LFS_ERR_INVAL; 3830 } 3831 3832 struct lfs_mlist dir; 3833 dir.next = lfs->mlist; 3834 if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { 3835 // must be empty before removal 3836 lfs_block_t pair[2]; 3837 lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0), 3838 LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair); 3839 if (res < 0) { 3840 return (int)res; 3841 } 3842 lfs_pair_fromle32(pair); 3843 3844 err = lfs_dir_fetch(lfs, &dir.m, pair); 3845 if (err) { 3846 return err; 3847 } 3848 3849 if (dir.m.count > 0 || dir.m.split) { 3850 return LFS_ERR_NOTEMPTY; 3851 } 3852 3853 // mark fs as orphaned 3854 err = lfs_fs_preporphans(lfs, +1); 3855 if (err) { 3856 return err; 3857 } 3858 3859 // I know it's crazy but yes, dir can be changed by our parent's 3860 // commit (if predecessor is child) 3861 dir.type = 0; 3862 dir.id = 0; 3863 lfs->mlist = &dir; 3864 } 3865 3866 // delete the entry 3867 err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( 3868 {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL})); 3869 if (err) { 3870 lfs->mlist = dir.next; 3871 return err; 3872 } 3873 3874 lfs->mlist = dir.next; 3875 if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { 3876 // fix orphan 3877 err = lfs_fs_preporphans(lfs, -1); 3878 if (err) { 3879 return err; 3880 } 3881 3882 err = lfs_fs_pred(lfs, dir.m.pair, &cwd); 3883 if (err) { 3884 return err; 3885 } 3886 3887 err = lfs_dir_drop(lfs, &cwd, &dir.m); 3888 if (err) { 3889 return err; 3890 } 3891 } 3892 3893 return 0; 3894} 3895#endif 3896 3897#ifndef LFS_READONLY 3898static int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) { 3899 // deorphan if we haven't yet, needed at most once after poweron 3900 int err = lfs_fs_forceconsistency(lfs); 3901 if (err) { 3902 return err; 3903 } 3904 3905 // find old entry 3906 lfs_mdir_t oldcwd; 3907 lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL); 3908 if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) { 3909 return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL; 3910 } 3911 3912 // find new entry 3913 lfs_mdir_t newcwd; 3914 uint16_t newid; 3915 lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid); 3916 if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) && 3917 !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) { 3918 return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL; 3919 } 3920 3921 // if we're in the same pair there's a few special cases... 3922 bool samepair = (lfs_pair_cmp(oldcwd.pair, newcwd.pair) == 0); 3923 uint16_t newoldid = lfs_tag_id(oldtag); 3924 3925 struct lfs_mlist prevdir; 3926 prevdir.next = lfs->mlist; 3927 if (prevtag == LFS_ERR_NOENT) { 3928 // check that name fits 3929 lfs_size_t nlen = strlen(newpath); 3930 if (nlen > lfs->name_max) { 3931 return LFS_ERR_NAMETOOLONG; 3932 } 3933 3934 // there is a small chance we are being renamed in the same 3935 // directory/ to an id less than our old id, the global update 3936 // to handle this is a bit messy 3937 if (samepair && newid <= newoldid) { 3938 newoldid += 1; 3939 } 3940 } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) { 3941 return LFS_ERR_ISDIR; 3942 } else if (samepair && newid == newoldid) { 3943 // we're renaming to ourselves?? 3944 return 0; 3945 } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { 3946 // must be empty before removal 3947 lfs_block_t prevpair[2]; 3948 lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0), 3949 LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair); 3950 if (res < 0) { 3951 return (int)res; 3952 } 3953 lfs_pair_fromle32(prevpair); 3954 3955 // must be empty before removal 3956 err = lfs_dir_fetch(lfs, &prevdir.m, prevpair); 3957 if (err) { 3958 return err; 3959 } 3960 3961 if (prevdir.m.count > 0 || prevdir.m.split) { 3962 return LFS_ERR_NOTEMPTY; 3963 } 3964 3965 // mark fs as orphaned 3966 err = lfs_fs_preporphans(lfs, +1); 3967 if (err) { 3968 return err; 3969 } 3970 3971 // I know it's crazy but yes, dir can be changed by our parent's 3972 // commit (if predecessor is child) 3973 prevdir.type = 0; 3974 prevdir.id = 0; 3975 lfs->mlist = &prevdir; 3976 } 3977 3978 if (!samepair) { 3979 lfs_fs_prepmove(lfs, newoldid, oldcwd.pair); 3980 } 3981 3982 // move over all attributes 3983 err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS( 3984 {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT, 3985 LFS_TYPE_DELETE, newid, 0), NULL}, 3986 {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL}, 3987 {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath}, 3988 {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd}, 3989 {LFS_MKTAG_IF(samepair, 3990 LFS_TYPE_DELETE, newoldid, 0), NULL})); 3991 if (err) { 3992 lfs->mlist = prevdir.next; 3993 return err; 3994 } 3995 3996 // let commit clean up after move (if we're different! otherwise move 3997 // logic already fixed it for us) 3998 if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) { 3999 // prep gstate and delete move id 4000 lfs_fs_prepmove(lfs, 0x3ff, NULL); 4001 err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS( 4002 {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0), NULL})); 4003 if (err) { 4004 lfs->mlist = prevdir.next; 4005 return err; 4006 } 4007 } 4008 4009 lfs->mlist = prevdir.next; 4010 if (prevtag != LFS_ERR_NOENT 4011 && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { 4012 // fix orphan 4013 err = lfs_fs_preporphans(lfs, -1); 4014 if (err) { 4015 return err; 4016 } 4017 4018 err = lfs_fs_pred(lfs, prevdir.m.pair, &newcwd); 4019 if (err) { 4020 return err; 4021 } 4022 4023 err = lfs_dir_drop(lfs, &newcwd, &prevdir.m); 4024 if (err) { 4025 return err; 4026 } 4027 } 4028 4029 return 0; 4030} 4031#endif 4032 4033static lfs_ssize_t lfs_rawgetattr(lfs_t *lfs, const char *path, 4034 uint8_t type, void *buffer, lfs_size_t size) { 4035 lfs_mdir_t cwd; 4036 lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); 4037 if (tag < 0) { 4038 return tag; 4039 } 4040 4041 uint16_t id = lfs_tag_id(tag); 4042 if (id == 0x3ff) { 4043 // special case for root 4044 id = 0; 4045 int err = lfs_dir_fetch(lfs, &cwd, lfs->root); 4046 if (err) { 4047 return err; 4048 } 4049 } 4050 4051 tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0), 4052 LFS_MKTAG(LFS_TYPE_USERATTR + type, 4053 id, lfs_min(size, lfs->attr_max)), 4054 buffer); 4055 if (tag < 0) { 4056 if (tag == LFS_ERR_NOENT) { 4057 return LFS_ERR_NOATTR; 4058 } 4059 4060 return tag; 4061 } 4062 4063 return lfs_tag_size(tag); 4064} 4065 4066#ifndef LFS_READONLY 4067static int lfs_commitattr(lfs_t *lfs, const char *path, 4068 uint8_t type, const void *buffer, lfs_size_t size) { 4069 lfs_mdir_t cwd; 4070 lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL); 4071 if (tag < 0) { 4072 return tag; 4073 } 4074 4075 uint16_t id = lfs_tag_id(tag); 4076 if (id == 0x3ff) { 4077 // special case for root 4078 id = 0; 4079 int err = lfs_dir_fetch(lfs, &cwd, lfs->root); 4080 if (err) { 4081 return err; 4082 } 4083 } 4084 4085 return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS( 4086 {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer})); 4087} 4088#endif 4089 4090#ifndef LFS_READONLY 4091static int lfs_rawsetattr(lfs_t *lfs, const char *path, 4092 uint8_t type, const void *buffer, lfs_size_t size) { 4093 if (size > lfs->attr_max) { 4094 return LFS_ERR_NOSPC; 4095 } 4096 4097 return lfs_commitattr(lfs, path, type, buffer, size); 4098} 4099#endif 4100 4101#ifndef LFS_READONLY 4102static int lfs_rawremoveattr(lfs_t *lfs, const char *path, uint8_t type) { 4103 return lfs_commitattr(lfs, path, type, NULL, 0x3ff); 4104} 4105#endif 4106 4107 4108/// Filesystem operations /// 4109static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { 4110 lfs->cfg = cfg; 4111 lfs->block_count = cfg->block_count; // May be 0 4112 int err = 0; 4113 4114#ifdef LFS_MULTIVERSION 4115 // this driver only supports minor version < current minor version 4116 LFS_ASSERT(!lfs->cfg->disk_version || ( 4117 (0xffff & (lfs->cfg->disk_version >> 16)) 4118 == LFS_DISK_VERSION_MAJOR 4119 && (0xffff & (lfs->cfg->disk_version >> 0)) 4120 <= LFS_DISK_VERSION_MINOR)); 4121#endif 4122 4123 // check that bool is a truthy-preserving type 4124 // 4125 // note the most common reason for this failure is a before-c99 compiler, 4126 // which littlefs currently does not support 4127 LFS_ASSERT((bool)0x80000000); 4128 4129 // validate that the lfs-cfg sizes were initiated properly before 4130 // performing any arithmetic logics with them 4131 LFS_ASSERT(lfs->cfg->read_size != 0); 4132 LFS_ASSERT(lfs->cfg->prog_size != 0); 4133 LFS_ASSERT(lfs->cfg->cache_size != 0); 4134 4135 // check that block size is a multiple of cache size is a multiple 4136 // of prog and read sizes 4137 LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0); 4138 LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0); 4139 LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0); 4140 4141 // check that the block size is large enough to fit all ctz pointers 4142 LFS_ASSERT(lfs->cfg->block_size >= 128); 4143 // this is the exact calculation for all ctz pointers, if this fails 4144 // and the simpler assert above does not, math must be broken 4145 LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4)) 4146 <= lfs->cfg->block_size); 4147 4148 // block_cycles = 0 is no longer supported. 4149 // 4150 // block_cycles is the number of erase cycles before littlefs evicts 4151 // metadata logs as a part of wear leveling. Suggested values are in the 4152 // range of 100-1000, or set block_cycles to -1 to disable block-level 4153 // wear-leveling. 4154 LFS_ASSERT(lfs->cfg->block_cycles != 0); 4155 4156 4157 // setup read cache 4158 if (lfs->cfg->read_buffer) { 4159 lfs->rcache.buffer = lfs->cfg->read_buffer; 4160 } else { 4161 lfs->rcache.buffer = lfs_malloc(lfs->cfg->cache_size); 4162 if (!lfs->rcache.buffer) { 4163 err = LFS_ERR_NOMEM; 4164 goto cleanup; 4165 } 4166 } 4167 4168 // setup program cache 4169 if (lfs->cfg->prog_buffer) { 4170 lfs->pcache.buffer = lfs->cfg->prog_buffer; 4171 } else { 4172 lfs->pcache.buffer = lfs_malloc(lfs->cfg->cache_size); 4173 if (!lfs->pcache.buffer) { 4174 err = LFS_ERR_NOMEM; 4175 goto cleanup; 4176 } 4177 } 4178 4179 // zero to avoid information leaks 4180 lfs_cache_zero(lfs, &lfs->rcache); 4181 lfs_cache_zero(lfs, &lfs->pcache); 4182 4183 // setup lookahead, must be multiple of 64-bits, 32-bit aligned 4184 LFS_ASSERT(lfs->cfg->lookahead_size > 0); 4185 LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 && 4186 (uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0); 4187 if (lfs->cfg->lookahead_buffer) { 4188 lfs->free.buffer = lfs->cfg->lookahead_buffer; 4189 } else { 4190 lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size); 4191 if (!lfs->free.buffer) { 4192 err = LFS_ERR_NOMEM; 4193 goto cleanup; 4194 } 4195 } 4196 4197 // check that the size limits are sane 4198 LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX); 4199 lfs->name_max = lfs->cfg->name_max; 4200 if (!lfs->name_max) { 4201 lfs->name_max = LFS_NAME_MAX; 4202 } 4203 4204 LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX); 4205 lfs->file_max = lfs->cfg->file_max; 4206 if (!lfs->file_max) { 4207 lfs->file_max = LFS_FILE_MAX; 4208 } 4209 4210 LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX); 4211 lfs->attr_max = lfs->cfg->attr_max; 4212 if (!lfs->attr_max) { 4213 lfs->attr_max = LFS_ATTR_MAX; 4214 } 4215 4216 LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size); 4217 4218 // setup default state 4219 lfs->root[0] = LFS_BLOCK_NULL; 4220 lfs->root[1] = LFS_BLOCK_NULL; 4221 lfs->mlist = NULL; 4222 lfs->seed = 0; 4223 lfs->gdisk = (lfs_gstate_t){0}; 4224 lfs->gstate = (lfs_gstate_t){0}; 4225 lfs->gdelta = (lfs_gstate_t){0}; 4226#ifdef LFS_MIGRATE 4227 lfs->lfs1 = NULL; 4228#endif 4229 4230 return 0; 4231 4232cleanup: 4233 lfs_deinit(lfs); 4234 return err; 4235} 4236 4237static int lfs_deinit(lfs_t *lfs) { 4238 // free allocated memory 4239 if (!lfs->cfg->read_buffer) { 4240 lfs_free(lfs->rcache.buffer); 4241 } 4242 4243 if (!lfs->cfg->prog_buffer) { 4244 lfs_free(lfs->pcache.buffer); 4245 } 4246 4247 if (!lfs->cfg->lookahead_buffer) { 4248 lfs_free(lfs->free.buffer); 4249 } 4250 4251 return 0; 4252} 4253 4254 4255 4256#ifndef LFS_READONLY 4257static int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) { 4258 int err = 0; 4259 { 4260 err = lfs_init(lfs, cfg); 4261 if (err) { 4262 return err; 4263 } 4264 4265 LFS_ASSERT(cfg->block_count != 0); 4266 4267 // create free lookahead 4268 memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size); 4269 lfs->free.off = 0; 4270 lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, 4271 lfs->block_count); 4272 lfs->free.i = 0; 4273 lfs_alloc_ack(lfs); 4274 4275 // create root dir 4276 lfs_mdir_t root; 4277 err = lfs_dir_alloc(lfs, &root); 4278 if (err) { 4279 goto cleanup; 4280 } 4281 4282 // write one superblock 4283 lfs_superblock_t superblock = { 4284 .version = lfs_fs_disk_version(lfs), 4285 .block_size = lfs->cfg->block_size, 4286 .block_count = lfs->block_count, 4287 .name_max = lfs->name_max, 4288 .file_max = lfs->file_max, 4289 .attr_max = lfs->attr_max, 4290 }; 4291 4292 lfs_superblock_tole32(&superblock); 4293 err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( 4294 {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, 4295 {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, 4296 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 4297 &superblock})); 4298 if (err) { 4299 goto cleanup; 4300 } 4301 4302 // force compaction to prevent accidentally mounting any 4303 // older version of littlefs that may live on disk 4304 root.erased = false; 4305 err = lfs_dir_commit(lfs, &root, NULL, 0); 4306 if (err) { 4307 goto cleanup; 4308 } 4309 4310 // sanity check that fetch works 4311 err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); 4312 if (err) { 4313 goto cleanup; 4314 } 4315 } 4316 4317cleanup: 4318 lfs_deinit(lfs); 4319 return err; 4320 4321} 4322#endif 4323 4324static int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) { 4325 int err = lfs_init(lfs, cfg); 4326 if (err) { 4327 return err; 4328 } 4329 4330 // scan directory blocks for superblock and any global updates 4331 lfs_mdir_t dir = {.tail = {0, 1}}; 4332 lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; 4333 lfs_size_t tortoise_i = 1; 4334 lfs_size_t tortoise_period = 1; 4335 while (!lfs_pair_isnull(dir.tail)) { 4336 // detect cycles with Brent's algorithm 4337 if (lfs_pair_issync(dir.tail, tortoise)) { 4338 LFS_WARN("Cycle detected in tail list"); 4339 err = LFS_ERR_CORRUPT; 4340 goto cleanup; 4341 } 4342 if (tortoise_i == tortoise_period) { 4343 tortoise[0] = dir.tail[0]; 4344 tortoise[1] = dir.tail[1]; 4345 tortoise_i = 0; 4346 tortoise_period *= 2; 4347 } 4348 tortoise_i += 1; 4349 4350 // fetch next block in tail list 4351 lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail, 4352 LFS_MKTAG(0x7ff, 0x3ff, 0), 4353 LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), 4354 NULL, 4355 lfs_dir_find_match, &(struct lfs_dir_find_match){ 4356 lfs, "littlefs", 8}); 4357 if (tag < 0) { 4358 err = tag; 4359 goto cleanup; 4360 } 4361 4362 // has superblock? 4363 if (tag && !lfs_tag_isdelete(tag)) { 4364 // update root 4365 lfs->root[0] = dir.pair[0]; 4366 lfs->root[1] = dir.pair[1]; 4367 4368 // grab superblock 4369 lfs_superblock_t superblock; 4370 tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), 4371 LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 4372 &superblock); 4373 if (tag < 0) { 4374 err = tag; 4375 goto cleanup; 4376 } 4377 lfs_superblock_fromle32(&superblock); 4378 4379 // check version 4380 uint16_t major_version = (0xffff & (superblock.version >> 16)); 4381 uint16_t minor_version = (0xffff & (superblock.version >> 0)); 4382 if (major_version != lfs_fs_disk_version_major(lfs) 4383 || minor_version > lfs_fs_disk_version_minor(lfs)) { 4384 LFS_ERROR("Invalid version " 4385 "v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16, 4386 major_version, 4387 minor_version, 4388 lfs_fs_disk_version_major(lfs), 4389 lfs_fs_disk_version_minor(lfs)); 4390 err = LFS_ERR_INVAL; 4391 goto cleanup; 4392 } 4393 4394 // found older minor version? set an in-device only bit in the 4395 // gstate so we know we need to rewrite the superblock before 4396 // the first write 4397 if (minor_version < lfs_fs_disk_version_minor(lfs)) { 4398 LFS_DEBUG("Found older minor version " 4399 "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, 4400 major_version, 4401 minor_version, 4402 lfs_fs_disk_version_major(lfs), 4403 lfs_fs_disk_version_minor(lfs)); 4404 // note this bit is reserved on disk, so fetching more gstate 4405 // will not interfere here 4406 lfs_fs_prepsuperblock(lfs, true); 4407 } 4408 4409 // check superblock configuration 4410 if (superblock.name_max) { 4411 if (superblock.name_max > lfs->name_max) { 4412 LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")", 4413 superblock.name_max, lfs->name_max); 4414 err = LFS_ERR_INVAL; 4415 goto cleanup; 4416 } 4417 4418 lfs->name_max = superblock.name_max; 4419 } 4420 4421 if (superblock.file_max) { 4422 if (superblock.file_max > lfs->file_max) { 4423 LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")", 4424 superblock.file_max, lfs->file_max); 4425 err = LFS_ERR_INVAL; 4426 goto cleanup; 4427 } 4428 4429 lfs->file_max = superblock.file_max; 4430 } 4431 4432 if (superblock.attr_max) { 4433 if (superblock.attr_max > lfs->attr_max) { 4434 LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")", 4435 superblock.attr_max, lfs->attr_max); 4436 err = LFS_ERR_INVAL; 4437 goto cleanup; 4438 } 4439 4440 lfs->attr_max = superblock.attr_max; 4441 } 4442 4443 // this is where we get the block_count from disk if block_count=0 4444 if (lfs->cfg->block_count 4445 && superblock.block_count != lfs->cfg->block_count) { 4446 LFS_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")", 4447 superblock.block_count, lfs->cfg->block_count); 4448 err = LFS_ERR_INVAL; 4449 goto cleanup; 4450 } 4451 4452 lfs->block_count = superblock.block_count; 4453 4454 if (superblock.block_size != lfs->cfg->block_size) { 4455 LFS_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")", 4456 superblock.block_size, lfs->cfg->block_size); 4457 err = LFS_ERR_INVAL; 4458 goto cleanup; 4459 } 4460 } 4461 4462 // has gstate? 4463 err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate); 4464 if (err) { 4465 goto cleanup; 4466 } 4467 } 4468 4469 // update littlefs with gstate 4470 if (!lfs_gstate_iszero(&lfs->gstate)) { 4471 LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32, 4472 lfs->gstate.tag, 4473 lfs->gstate.pair[0], 4474 lfs->gstate.pair[1]); 4475 } 4476 lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag); 4477 lfs->gdisk = lfs->gstate; 4478 4479 // setup free lookahead, to distribute allocations uniformly across 4480 // boots, we start the allocator at a random location 4481 lfs->free.off = lfs->seed % lfs->block_count; 4482 lfs_alloc_drop(lfs); 4483 4484 return 0; 4485 4486cleanup: 4487 lfs_rawunmount(lfs); 4488 return err; 4489} 4490 4491static int lfs_rawunmount(lfs_t *lfs) { 4492 return lfs_deinit(lfs); 4493} 4494 4495 4496/// Filesystem filesystem operations /// 4497static int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { 4498 // if the superblock is up-to-date, we must be on the most recent 4499 // minor version of littlefs 4500 if (!lfs_gstate_needssuperblock(&lfs->gstate)) { 4501 fsinfo->disk_version = lfs_fs_disk_version(lfs); 4502 4503 // otherwise we need to read the minor version on disk 4504 } else { 4505 // fetch the superblock 4506 lfs_mdir_t dir; 4507 int err = lfs_dir_fetch(lfs, &dir, lfs->root); 4508 if (err) { 4509 return err; 4510 } 4511 4512 lfs_superblock_t superblock; 4513 lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0), 4514 LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 4515 &superblock); 4516 if (tag < 0) { 4517 return tag; 4518 } 4519 lfs_superblock_fromle32(&superblock); 4520 4521 // read the on-disk version 4522 fsinfo->disk_version = superblock.version; 4523 } 4524 4525 // filesystem geometry 4526 fsinfo->block_size = lfs->cfg->block_size; 4527 fsinfo->block_count = lfs->block_count; 4528 4529 // other on-disk configuration, we cache all of these for internal use 4530 fsinfo->name_max = lfs->name_max; 4531 fsinfo->file_max = lfs->file_max; 4532 fsinfo->attr_max = lfs->attr_max; 4533 4534 return 0; 4535} 4536 4537int lfs_fs_rawtraverse(lfs_t *lfs, 4538 int (*cb)(void *data, lfs_block_t block), void *data, 4539 bool includeorphans) { 4540 // iterate over metadata pairs 4541 lfs_mdir_t dir = {.tail = {0, 1}}; 4542 4543#ifdef LFS_MIGRATE 4544 // also consider v1 blocks during migration 4545 if (lfs->lfs1) { 4546 int err = lfs1_traverse(lfs, cb, data); 4547 if (err) { 4548 return err; 4549 } 4550 4551 dir.tail[0] = lfs->root[0]; 4552 dir.tail[1] = lfs->root[1]; 4553 } 4554#endif 4555 4556 lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; 4557 lfs_size_t tortoise_i = 1; 4558 lfs_size_t tortoise_period = 1; 4559 while (!lfs_pair_isnull(dir.tail)) { 4560 // detect cycles with Brent's algorithm 4561 if (lfs_pair_issync(dir.tail, tortoise)) { 4562 LFS_WARN("Cycle detected in tail list"); 4563 return LFS_ERR_CORRUPT; 4564 } 4565 if (tortoise_i == tortoise_period) { 4566 tortoise[0] = dir.tail[0]; 4567 tortoise[1] = dir.tail[1]; 4568 tortoise_i = 0; 4569 tortoise_period *= 2; 4570 } 4571 tortoise_i += 1; 4572 4573 for (int i = 0; i < 2; i++) { 4574 int err = cb(data, dir.tail[i]); 4575 if (err) { 4576 return err; 4577 } 4578 } 4579 4580 // iterate through ids in directory 4581 int err = lfs_dir_fetch(lfs, &dir, dir.tail); 4582 if (err) { 4583 return err; 4584 } 4585 4586 for (uint16_t id = 0; id < dir.count; id++) { 4587 struct lfs_ctz ctz; 4588 lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0), 4589 LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz); 4590 if (tag < 0) { 4591 if (tag == LFS_ERR_NOENT) { 4592 continue; 4593 } 4594 return tag; 4595 } 4596 lfs_ctz_fromle32(&ctz); 4597 4598 if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) { 4599 err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, 4600 ctz.head, ctz.size, cb, data); 4601 if (err) { 4602 return err; 4603 } 4604 } else if (includeorphans && 4605 lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { 4606 for (int i = 0; i < 2; i++) { 4607 err = cb(data, (&ctz.head)[i]); 4608 if (err) { 4609 return err; 4610 } 4611 } 4612 } 4613 } 4614 } 4615 4616#ifndef LFS_READONLY 4617 // iterate over any open files 4618 for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) { 4619 if (f->type != LFS_TYPE_REG) { 4620 continue; 4621 } 4622 4623 if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) { 4624 int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, 4625 f->ctz.head, f->ctz.size, cb, data); 4626 if (err) { 4627 return err; 4628 } 4629 } 4630 4631 if ((f->flags & LFS_F_WRITING) && !(f->flags & LFS_F_INLINE)) { 4632 int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache, 4633 f->block, f->pos, cb, data); 4634 if (err) { 4635 return err; 4636 } 4637 } 4638 } 4639#endif 4640 4641 return 0; 4642} 4643 4644#ifndef LFS_READONLY 4645static int lfs_fs_pred(lfs_t *lfs, 4646 const lfs_block_t pair[2], lfs_mdir_t *pdir) { 4647 // iterate over all directory directory entries 4648 pdir->tail[0] = 0; 4649 pdir->tail[1] = 1; 4650 lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; 4651 lfs_size_t tortoise_i = 1; 4652 lfs_size_t tortoise_period = 1; 4653 while (!lfs_pair_isnull(pdir->tail)) { 4654 // detect cycles with Brent's algorithm 4655 if (lfs_pair_issync(pdir->tail, tortoise)) { 4656 LFS_WARN("Cycle detected in tail list"); 4657 return LFS_ERR_CORRUPT; 4658 } 4659 if (tortoise_i == tortoise_period) { 4660 tortoise[0] = pdir->tail[0]; 4661 tortoise[1] = pdir->tail[1]; 4662 tortoise_i = 0; 4663 tortoise_period *= 2; 4664 } 4665 tortoise_i += 1; 4666 4667 if (lfs_pair_cmp(pdir->tail, pair) == 0) { 4668 return 0; 4669 } 4670 4671 int err = lfs_dir_fetch(lfs, pdir, pdir->tail); 4672 if (err) { 4673 return err; 4674 } 4675 } 4676 4677 return LFS_ERR_NOENT; 4678} 4679#endif 4680 4681#ifndef LFS_READONLY 4682struct lfs_fs_parent_match { 4683 lfs_t *lfs; 4684 const lfs_block_t pair[2]; 4685}; 4686#endif 4687 4688#ifndef LFS_READONLY 4689static int lfs_fs_parent_match(void *data, 4690 lfs_tag_t tag, const void *buffer) { 4691 struct lfs_fs_parent_match *find = data; 4692 lfs_t *lfs = find->lfs; 4693 const struct lfs_diskoff *disk = buffer; 4694 (void)tag; 4695 4696 lfs_block_t child[2]; 4697 int err = lfs_bd_read(lfs, 4698 &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, 4699 disk->block, disk->off, &child, sizeof(child)); 4700 if (err) { 4701 return err; 4702 } 4703 4704 lfs_pair_fromle32(child); 4705 return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT; 4706} 4707#endif 4708 4709#ifndef LFS_READONLY 4710static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2], 4711 lfs_mdir_t *parent) { 4712 // use fetchmatch with callback to find pairs 4713 parent->tail[0] = 0; 4714 parent->tail[1] = 1; 4715 lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL}; 4716 lfs_size_t tortoise_i = 1; 4717 lfs_size_t tortoise_period = 1; 4718 while (!lfs_pair_isnull(parent->tail)) { 4719 // detect cycles with Brent's algorithm 4720 if (lfs_pair_issync(parent->tail, tortoise)) { 4721 LFS_WARN("Cycle detected in tail list"); 4722 return LFS_ERR_CORRUPT; 4723 } 4724 if (tortoise_i == tortoise_period) { 4725 tortoise[0] = parent->tail[0]; 4726 tortoise[1] = parent->tail[1]; 4727 tortoise_i = 0; 4728 tortoise_period *= 2; 4729 } 4730 tortoise_i += 1; 4731 4732 lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail, 4733 LFS_MKTAG(0x7ff, 0, 0x3ff), 4734 LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8), 4735 NULL, 4736 lfs_fs_parent_match, &(struct lfs_fs_parent_match){ 4737 lfs, {pair[0], pair[1]}}); 4738 if (tag && tag != LFS_ERR_NOENT) { 4739 return tag; 4740 } 4741 } 4742 4743 return LFS_ERR_NOENT; 4744} 4745#endif 4746 4747static void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock) { 4748 lfs->gstate.tag = (lfs->gstate.tag & ~LFS_MKTAG(0, 0, 0x200)) 4749 | (uint32_t)needssuperblock << 9; 4750} 4751 4752#ifndef LFS_READONLY 4753static int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { 4754 LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0); 4755 LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x1ff || orphans <= 0); 4756 lfs->gstate.tag += orphans; 4757 lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) | 4758 ((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31)); 4759 4760 return 0; 4761} 4762#endif 4763 4764#ifndef LFS_READONLY 4765static void lfs_fs_prepmove(lfs_t *lfs, 4766 uint16_t id, const lfs_block_t pair[2]) { 4767 lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x7ff, 0x3ff, 0)) | 4768 ((id != 0x3ff) ? LFS_MKTAG(LFS_TYPE_DELETE, id, 0) : 0)); 4769 lfs->gstate.pair[0] = (id != 0x3ff) ? pair[0] : 0; 4770 lfs->gstate.pair[1] = (id != 0x3ff) ? pair[1] : 0; 4771} 4772#endif 4773 4774#ifndef LFS_READONLY 4775static int lfs_fs_desuperblock(lfs_t *lfs) { 4776 if (!lfs_gstate_needssuperblock(&lfs->gstate)) { 4777 return 0; 4778 } 4779 4780 LFS_DEBUG("Rewriting superblock {0x%"PRIx32", 0x%"PRIx32"}", 4781 lfs->root[0], 4782 lfs->root[1]); 4783 4784 lfs_mdir_t root; 4785 int err = lfs_dir_fetch(lfs, &root, lfs->root); 4786 if (err) { 4787 return err; 4788 } 4789 4790 // write a new superblock 4791 lfs_superblock_t superblock = { 4792 .version = lfs_fs_disk_version(lfs), 4793 .block_size = lfs->cfg->block_size, 4794 .block_count = lfs->block_count, 4795 .name_max = lfs->name_max, 4796 .file_max = lfs->file_max, 4797 .attr_max = lfs->attr_max, 4798 }; 4799 4800 lfs_superblock_tole32(&superblock); 4801 err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( 4802 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 4803 &superblock})); 4804 if (err) { 4805 return err; 4806 } 4807 4808 lfs_fs_prepsuperblock(lfs, false); 4809 return 0; 4810} 4811#endif 4812 4813#ifndef LFS_READONLY 4814static int lfs_fs_demove(lfs_t *lfs) { 4815 if (!lfs_gstate_hasmove(&lfs->gdisk)) { 4816 return 0; 4817 } 4818 4819 // Fix bad moves 4820 LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16, 4821 lfs->gdisk.pair[0], 4822 lfs->gdisk.pair[1], 4823 lfs_tag_id(lfs->gdisk.tag)); 4824 4825 // no other gstate is supported at this time, so if we found something else 4826 // something most likely went wrong in gstate calculation 4827 LFS_ASSERT(lfs_tag_type3(lfs->gdisk.tag) == LFS_TYPE_DELETE); 4828 4829 // fetch and delete the moved entry 4830 lfs_mdir_t movedir; 4831 int err = lfs_dir_fetch(lfs, &movedir, lfs->gdisk.pair); 4832 if (err) { 4833 return err; 4834 } 4835 4836 // prep gstate and delete move id 4837 uint16_t moveid = lfs_tag_id(lfs->gdisk.tag); 4838 lfs_fs_prepmove(lfs, 0x3ff, NULL); 4839 err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS( 4840 {LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0), NULL})); 4841 if (err) { 4842 return err; 4843 } 4844 4845 return 0; 4846} 4847#endif 4848 4849#ifndef LFS_READONLY 4850static int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) { 4851 if (!lfs_gstate_hasorphans(&lfs->gstate)) { 4852 return 0; 4853 } 4854 4855 // Check for orphans in two separate passes: 4856 // - 1 for half-orphans (relocations) 4857 // - 2 for full-orphans (removes/renames) 4858 // 4859 // Two separate passes are needed as half-orphans can contain outdated 4860 // references to full-orphans, effectively hiding them from the deorphan 4861 // search. 4862 // 4863 int pass = 0; 4864 while (pass < 2) { 4865 // Fix any orphans 4866 lfs_mdir_t pdir = {.split = true, .tail = {0, 1}}; 4867 lfs_mdir_t dir; 4868 bool moreorphans = false; 4869 4870 // iterate over all directory directory entries 4871 while (!lfs_pair_isnull(pdir.tail)) { 4872 int err = lfs_dir_fetch(lfs, &dir, pdir.tail); 4873 if (err) { 4874 return err; 4875 } 4876 4877 // check head blocks for orphans 4878 if (!pdir.split) { 4879 // check if we have a parent 4880 lfs_mdir_t parent; 4881 lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent); 4882 if (tag < 0 && tag != LFS_ERR_NOENT) { 4883 return tag; 4884 } 4885 4886 if (pass == 0 && tag != LFS_ERR_NOENT) { 4887 lfs_block_t pair[2]; 4888 lfs_stag_t state = lfs_dir_get(lfs, &parent, 4889 LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair); 4890 if (state < 0) { 4891 return state; 4892 } 4893 lfs_pair_fromle32(pair); 4894 4895 if (!lfs_pair_issync(pair, pdir.tail)) { 4896 // we have desynced 4897 LFS_DEBUG("Fixing half-orphan " 4898 "{0x%"PRIx32", 0x%"PRIx32"} " 4899 "-> {0x%"PRIx32", 0x%"PRIx32"}", 4900 pdir.tail[0], pdir.tail[1], pair[0], pair[1]); 4901 4902 // fix pending move in this pair? this looks like an 4903 // optimization but is in fact _required_ since 4904 // relocating may outdate the move. 4905 uint16_t moveid = 0x3ff; 4906 if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) { 4907 moveid = lfs_tag_id(lfs->gstate.tag); 4908 LFS_DEBUG("Fixing move while fixing orphans " 4909 "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n", 4910 pdir.pair[0], pdir.pair[1], moveid); 4911 lfs_fs_prepmove(lfs, 0x3ff, NULL); 4912 } 4913 4914 lfs_pair_tole32(pair); 4915 state = lfs_dir_orphaningcommit(lfs, &pdir, LFS_MKATTRS( 4916 {LFS_MKTAG_IF(moveid != 0x3ff, 4917 LFS_TYPE_DELETE, moveid, 0), NULL}, 4918 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), 4919 pair})); 4920 lfs_pair_fromle32(pair); 4921 if (state < 0) { 4922 return state; 4923 } 4924 4925 // did our commit create more orphans? 4926 if (state == LFS_OK_ORPHANED) { 4927 moreorphans = true; 4928 } 4929 4930 // refetch tail 4931 continue; 4932 } 4933 } 4934 4935 // note we only check for full orphans if we may have had a 4936 // power-loss, otherwise orphans are created intentionally 4937 // during operations such as lfs_mkdir 4938 if (pass == 1 && tag == LFS_ERR_NOENT && powerloss) { 4939 // we are an orphan 4940 LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}", 4941 pdir.tail[0], pdir.tail[1]); 4942 4943 // steal state 4944 err = lfs_dir_getgstate(lfs, &dir, &lfs->gdelta); 4945 if (err) { 4946 return err; 4947 } 4948 4949 // steal tail 4950 lfs_pair_tole32(dir.tail); 4951 int state = lfs_dir_orphaningcommit(lfs, &pdir, LFS_MKATTRS( 4952 {LFS_MKTAG(LFS_TYPE_TAIL + dir.split, 0x3ff, 8), 4953 dir.tail})); 4954 lfs_pair_fromle32(dir.tail); 4955 if (state < 0) { 4956 return state; 4957 } 4958 4959 // did our commit create more orphans? 4960 if (state == LFS_OK_ORPHANED) { 4961 moreorphans = true; 4962 } 4963 4964 // refetch tail 4965 continue; 4966 } 4967 } 4968 4969 pdir = dir; 4970 } 4971 4972 pass = moreorphans ? 0 : pass+1; 4973 } 4974 4975 // mark orphans as fixed 4976 return lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); 4977} 4978#endif 4979 4980#ifndef LFS_READONLY 4981static int lfs_fs_forceconsistency(lfs_t *lfs) { 4982 int err = lfs_fs_desuperblock(lfs); 4983 if (err) { 4984 return err; 4985 } 4986 4987 err = lfs_fs_demove(lfs); 4988 if (err) { 4989 return err; 4990 } 4991 4992 err = lfs_fs_deorphan(lfs, true); 4993 if (err) { 4994 return err; 4995 } 4996 4997 return 0; 4998} 4999#endif 5000 5001#ifndef LFS_READONLY 5002int lfs_fs_rawmkconsistent(lfs_t *lfs) { 5003 // lfs_fs_forceconsistency does most of the work here 5004 int err = lfs_fs_forceconsistency(lfs); 5005 if (err) { 5006 return err; 5007 } 5008 5009 // do we have any pending gstate? 5010 lfs_gstate_t delta = {0}; 5011 lfs_gstate_xor(&delta, &lfs->gdisk); 5012 lfs_gstate_xor(&delta, &lfs->gstate); 5013 if (!lfs_gstate_iszero(&delta)) { 5014 // lfs_dir_commit will implicitly write out any pending gstate 5015 lfs_mdir_t root; 5016 err = lfs_dir_fetch(lfs, &root, lfs->root); 5017 if (err) { 5018 return err; 5019 } 5020 5021 err = lfs_dir_commit(lfs, &root, NULL, 0); 5022 if (err) { 5023 return err; 5024 } 5025 } 5026 5027 return 0; 5028} 5029#endif 5030 5031static int lfs_fs_size_count(void *p, lfs_block_t block) { 5032 (void)block; 5033 lfs_size_t *size = p; 5034 *size += 1; 5035 return 0; 5036} 5037 5038static lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) { 5039 lfs_size_t size = 0; 5040 int err = lfs_fs_rawtraverse(lfs, lfs_fs_size_count, &size, false); 5041 if (err) { 5042 return err; 5043 } 5044 5045 return size; 5046} 5047 5048#ifndef LFS_READONLY 5049int lfs_fs_rawgrow(lfs_t *lfs, lfs_size_t block_count) { 5050 // shrinking is not supported 5051 LFS_ASSERT(block_count >= lfs->block_count); 5052 5053 if (block_count > lfs->block_count) { 5054 lfs->block_count = block_count; 5055 5056 // fetch the root 5057 lfs_mdir_t root; 5058 int err = lfs_dir_fetch(lfs, &root, lfs->root); 5059 if (err) { 5060 return err; 5061 } 5062 5063 // update the superblock 5064 lfs_superblock_t superblock; 5065 lfs_stag_t tag = lfs_dir_get(lfs, &root, LFS_MKTAG(0x7ff, 0x3ff, 0), 5066 LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 5067 &superblock); 5068 if (tag < 0) { 5069 return tag; 5070 } 5071 lfs_superblock_fromle32(&superblock); 5072 5073 superblock.block_count = lfs->block_count; 5074 5075 lfs_superblock_tole32(&superblock); 5076 err = lfs_dir_commit(lfs, &root, LFS_MKATTRS( 5077 {tag, &superblock})); 5078 if (err) { 5079 return err; 5080 } 5081 } 5082 5083 return 0; 5084} 5085#endif 5086 5087#ifdef LFS_MIGRATE 5088////// Migration from littelfs v1 below this ////// 5089 5090/// Version info /// 5091 5092// Software library version 5093// Major (top-nibble), incremented on backwards incompatible changes 5094// Minor (bottom-nibble), incremented on feature additions 5095#define LFS1_VERSION 0x00010007 5096#define LFS1_VERSION_MAJOR (0xffff & (LFS1_VERSION >> 16)) 5097#define LFS1_VERSION_MINOR (0xffff & (LFS1_VERSION >> 0)) 5098 5099// Version of On-disk data structures 5100// Major (top-nibble), incremented on backwards incompatible changes 5101// Minor (bottom-nibble), incremented on feature additions 5102#define LFS1_DISK_VERSION 0x00010001 5103#define LFS1_DISK_VERSION_MAJOR (0xffff & (LFS1_DISK_VERSION >> 16)) 5104#define LFS1_DISK_VERSION_MINOR (0xffff & (LFS1_DISK_VERSION >> 0)) 5105 5106 5107/// v1 Definitions /// 5108 5109// File types 5110enum lfs1_type { 5111 LFS1_TYPE_REG = 0x11, 5112 LFS1_TYPE_DIR = 0x22, 5113 LFS1_TYPE_SUPERBLOCK = 0x2e, 5114}; 5115 5116typedef struct lfs1 { 5117 lfs_block_t root[2]; 5118} lfs1_t; 5119 5120typedef struct lfs1_entry { 5121 lfs_off_t off; 5122 5123 struct lfs1_disk_entry { 5124 uint8_t type; 5125 uint8_t elen; 5126 uint8_t alen; 5127 uint8_t nlen; 5128 union { 5129 struct { 5130 lfs_block_t head; 5131 lfs_size_t size; 5132 } file; 5133 lfs_block_t dir[2]; 5134 } u; 5135 } d; 5136} lfs1_entry_t; 5137 5138typedef struct lfs1_dir { 5139 struct lfs1_dir *next; 5140 lfs_block_t pair[2]; 5141 lfs_off_t off; 5142 5143 lfs_block_t head[2]; 5144 lfs_off_t pos; 5145 5146 struct lfs1_disk_dir { 5147 uint32_t rev; 5148 lfs_size_t size; 5149 lfs_block_t tail[2]; 5150 } d; 5151} lfs1_dir_t; 5152 5153typedef struct lfs1_superblock { 5154 lfs_off_t off; 5155 5156 struct lfs1_disk_superblock { 5157 uint8_t type; 5158 uint8_t elen; 5159 uint8_t alen; 5160 uint8_t nlen; 5161 lfs_block_t root[2]; 5162 uint32_t block_size; 5163 uint32_t block_count; 5164 uint32_t version; 5165 char magic[8]; 5166 } d; 5167} lfs1_superblock_t; 5168 5169 5170/// Low-level wrappers v1->v2 /// 5171static void lfs1_crc(uint32_t *crc, const void *buffer, size_t size) { 5172 *crc = lfs_crc(*crc, buffer, size); 5173} 5174 5175static int lfs1_bd_read(lfs_t *lfs, lfs_block_t block, 5176 lfs_off_t off, void *buffer, lfs_size_t size) { 5177 // if we ever do more than writes to alternating pairs, 5178 // this may need to consider pcache 5179 return lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, size, 5180 block, off, buffer, size); 5181} 5182 5183static int lfs1_bd_crc(lfs_t *lfs, lfs_block_t block, 5184 lfs_off_t off, lfs_size_t size, uint32_t *crc) { 5185 for (lfs_off_t i = 0; i < size; i++) { 5186 uint8_t c; 5187 int err = lfs1_bd_read(lfs, block, off+i, &c, 1); 5188 if (err) { 5189 return err; 5190 } 5191 5192 lfs1_crc(crc, &c, 1); 5193 } 5194 5195 return 0; 5196} 5197 5198 5199/// Endian swapping functions /// 5200static void lfs1_dir_fromle32(struct lfs1_disk_dir *d) { 5201 d->rev = lfs_fromle32(d->rev); 5202 d->size = lfs_fromle32(d->size); 5203 d->tail[0] = lfs_fromle32(d->tail[0]); 5204 d->tail[1] = lfs_fromle32(d->tail[1]); 5205} 5206 5207static void lfs1_dir_tole32(struct lfs1_disk_dir *d) { 5208 d->rev = lfs_tole32(d->rev); 5209 d->size = lfs_tole32(d->size); 5210 d->tail[0] = lfs_tole32(d->tail[0]); 5211 d->tail[1] = lfs_tole32(d->tail[1]); 5212} 5213 5214static void lfs1_entry_fromle32(struct lfs1_disk_entry *d) { 5215 d->u.dir[0] = lfs_fromle32(d->u.dir[0]); 5216 d->u.dir[1] = lfs_fromle32(d->u.dir[1]); 5217} 5218 5219static void lfs1_entry_tole32(struct lfs1_disk_entry *d) { 5220 d->u.dir[0] = lfs_tole32(d->u.dir[0]); 5221 d->u.dir[1] = lfs_tole32(d->u.dir[1]); 5222} 5223 5224static void lfs1_superblock_fromle32(struct lfs1_disk_superblock *d) { 5225 d->root[0] = lfs_fromle32(d->root[0]); 5226 d->root[1] = lfs_fromle32(d->root[1]); 5227 d->block_size = lfs_fromle32(d->block_size); 5228 d->block_count = lfs_fromle32(d->block_count); 5229 d->version = lfs_fromle32(d->version); 5230} 5231 5232 5233///// Metadata pair and directory operations /// 5234static inline lfs_size_t lfs1_entry_size(const lfs1_entry_t *entry) { 5235 return 4 + entry->d.elen + entry->d.alen + entry->d.nlen; 5236} 5237 5238static int lfs1_dir_fetch(lfs_t *lfs, 5239 lfs1_dir_t *dir, const lfs_block_t pair[2]) { 5240 // copy out pair, otherwise may be aliasing dir 5241 const lfs_block_t tpair[2] = {pair[0], pair[1]}; 5242 bool valid = false; 5243 5244 // check both blocks for the most recent revision 5245 for (int i = 0; i < 2; i++) { 5246 struct lfs1_disk_dir test; 5247 int err = lfs1_bd_read(lfs, tpair[i], 0, &test, sizeof(test)); 5248 lfs1_dir_fromle32(&test); 5249 if (err) { 5250 if (err == LFS_ERR_CORRUPT) { 5251 continue; 5252 } 5253 return err; 5254 } 5255 5256 if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) { 5257 continue; 5258 } 5259 5260 if ((0x7fffffff & test.size) < sizeof(test)+4 || 5261 (0x7fffffff & test.size) > lfs->cfg->block_size) { 5262 continue; 5263 } 5264 5265 uint32_t crc = 0xffffffff; 5266 lfs1_dir_tole32(&test); 5267 lfs1_crc(&crc, &test, sizeof(test)); 5268 lfs1_dir_fromle32(&test); 5269 err = lfs1_bd_crc(lfs, tpair[i], sizeof(test), 5270 (0x7fffffff & test.size) - sizeof(test), &crc); 5271 if (err) { 5272 if (err == LFS_ERR_CORRUPT) { 5273 continue; 5274 } 5275 return err; 5276 } 5277 5278 if (crc != 0) { 5279 continue; 5280 } 5281 5282 valid = true; 5283 5284 // setup dir in case it's valid 5285 dir->pair[0] = tpair[(i+0) % 2]; 5286 dir->pair[1] = tpair[(i+1) % 2]; 5287 dir->off = sizeof(dir->d); 5288 dir->d = test; 5289 } 5290 5291 if (!valid) { 5292 LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}", 5293 tpair[0], tpair[1]); 5294 return LFS_ERR_CORRUPT; 5295 } 5296 5297 return 0; 5298} 5299 5300static int lfs1_dir_next(lfs_t *lfs, lfs1_dir_t *dir, lfs1_entry_t *entry) { 5301 while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) { 5302 if (!(0x80000000 & dir->d.size)) { 5303 entry->off = dir->off; 5304 return LFS_ERR_NOENT; 5305 } 5306 5307 int err = lfs1_dir_fetch(lfs, dir, dir->d.tail); 5308 if (err) { 5309 return err; 5310 } 5311 5312 dir->off = sizeof(dir->d); 5313 dir->pos += sizeof(dir->d) + 4; 5314 } 5315 5316 int err = lfs1_bd_read(lfs, dir->pair[0], dir->off, 5317 &entry->d, sizeof(entry->d)); 5318 lfs1_entry_fromle32(&entry->d); 5319 if (err) { 5320 return err; 5321 } 5322 5323 entry->off = dir->off; 5324 dir->off += lfs1_entry_size(entry); 5325 dir->pos += lfs1_entry_size(entry); 5326 return 0; 5327} 5328 5329/// littlefs v1 specific operations /// 5330int lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { 5331 if (lfs_pair_isnull(lfs->lfs1->root)) { 5332 return 0; 5333 } 5334 5335 // iterate over metadata pairs 5336 lfs1_dir_t dir; 5337 lfs1_entry_t entry; 5338 lfs_block_t cwd[2] = {0, 1}; 5339 5340 while (true) { 5341 for (int i = 0; i < 2; i++) { 5342 int err = cb(data, cwd[i]); 5343 if (err) { 5344 return err; 5345 } 5346 } 5347 5348 int err = lfs1_dir_fetch(lfs, &dir, cwd); 5349 if (err) { 5350 return err; 5351 } 5352 5353 // iterate over contents 5354 while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) { 5355 err = lfs1_bd_read(lfs, dir.pair[0], dir.off, 5356 &entry.d, sizeof(entry.d)); 5357 lfs1_entry_fromle32(&entry.d); 5358 if (err) { 5359 return err; 5360 } 5361 5362 dir.off += lfs1_entry_size(&entry); 5363 if ((0x70 & entry.d.type) == (0x70 & LFS1_TYPE_REG)) { 5364 err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache, 5365 entry.d.u.file.head, entry.d.u.file.size, cb, data); 5366 if (err) { 5367 return err; 5368 } 5369 } 5370 } 5371 5372 // we also need to check if we contain a threaded v2 directory 5373 lfs_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}}; 5374 while (dir2.split) { 5375 err = lfs_dir_fetch(lfs, &dir2, dir2.tail); 5376 if (err) { 5377 break; 5378 } 5379 5380 for (int i = 0; i < 2; i++) { 5381 err = cb(data, dir2.pair[i]); 5382 if (err) { 5383 return err; 5384 } 5385 } 5386 } 5387 5388 cwd[0] = dir.d.tail[0]; 5389 cwd[1] = dir.d.tail[1]; 5390 5391 if (lfs_pair_isnull(cwd)) { 5392 break; 5393 } 5394 } 5395 5396 return 0; 5397} 5398 5399static int lfs1_moved(lfs_t *lfs, const void *e) { 5400 if (lfs_pair_isnull(lfs->lfs1->root)) { 5401 return 0; 5402 } 5403 5404 // skip superblock 5405 lfs1_dir_t cwd; 5406 int err = lfs1_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1}); 5407 if (err) { 5408 return err; 5409 } 5410 5411 // iterate over all directory directory entries 5412 lfs1_entry_t entry; 5413 while (!lfs_pair_isnull(cwd.d.tail)) { 5414 err = lfs1_dir_fetch(lfs, &cwd, cwd.d.tail); 5415 if (err) { 5416 return err; 5417 } 5418 5419 while (true) { 5420 err = lfs1_dir_next(lfs, &cwd, &entry); 5421 if (err && err != LFS_ERR_NOENT) { 5422 return err; 5423 } 5424 5425 if (err == LFS_ERR_NOENT) { 5426 break; 5427 } 5428 5429 if (!(0x80 & entry.d.type) && 5430 memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) { 5431 return true; 5432 } 5433 } 5434 } 5435 5436 return false; 5437} 5438 5439/// Filesystem operations /// 5440static int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1, 5441 const struct lfs_config *cfg) { 5442 int err = 0; 5443 { 5444 err = lfs_init(lfs, cfg); 5445 if (err) { 5446 return err; 5447 } 5448 5449 lfs->lfs1 = lfs1; 5450 lfs->lfs1->root[0] = LFS_BLOCK_NULL; 5451 lfs->lfs1->root[1] = LFS_BLOCK_NULL; 5452 5453 // setup free lookahead 5454 lfs->free.off = 0; 5455 lfs->free.size = 0; 5456 lfs->free.i = 0; 5457 lfs_alloc_ack(lfs); 5458 5459 // load superblock 5460 lfs1_dir_t dir; 5461 lfs1_superblock_t superblock; 5462 err = lfs1_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); 5463 if (err && err != LFS_ERR_CORRUPT) { 5464 goto cleanup; 5465 } 5466 5467 if (!err) { 5468 err = lfs1_bd_read(lfs, dir.pair[0], sizeof(dir.d), 5469 &superblock.d, sizeof(superblock.d)); 5470 lfs1_superblock_fromle32(&superblock.d); 5471 if (err) { 5472 goto cleanup; 5473 } 5474 5475 lfs->lfs1->root[0] = superblock.d.root[0]; 5476 lfs->lfs1->root[1] = superblock.d.root[1]; 5477 } 5478 5479 if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { 5480 LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}", 5481 0, 1); 5482 err = LFS_ERR_CORRUPT; 5483 goto cleanup; 5484 } 5485 5486 uint16_t major_version = (0xffff & (superblock.d.version >> 16)); 5487 uint16_t minor_version = (0xffff & (superblock.d.version >> 0)); 5488 if ((major_version != LFS1_DISK_VERSION_MAJOR || 5489 minor_version > LFS1_DISK_VERSION_MINOR)) { 5490 LFS_ERROR("Invalid version v%d.%d", major_version, minor_version); 5491 err = LFS_ERR_INVAL; 5492 goto cleanup; 5493 } 5494 5495 return 0; 5496 } 5497 5498cleanup: 5499 lfs_deinit(lfs); 5500 return err; 5501} 5502 5503static int lfs1_unmount(lfs_t *lfs) { 5504 return lfs_deinit(lfs); 5505} 5506 5507/// v1 migration /// 5508static int lfs_rawmigrate(lfs_t *lfs, const struct lfs_config *cfg) { 5509 struct lfs1 lfs1; 5510 5511 // Indeterminate filesystem size not allowed for migration. 5512 LFS_ASSERT(cfg->block_count != 0); 5513 5514 int err = lfs1_mount(lfs, &lfs1, cfg); 5515 if (err) { 5516 return err; 5517 } 5518 5519 { 5520 // iterate through each directory, copying over entries 5521 // into new directory 5522 lfs1_dir_t dir1; 5523 lfs_mdir_t dir2; 5524 dir1.d.tail[0] = lfs->lfs1->root[0]; 5525 dir1.d.tail[1] = lfs->lfs1->root[1]; 5526 while (!lfs_pair_isnull(dir1.d.tail)) { 5527 // iterate old dir 5528 err = lfs1_dir_fetch(lfs, &dir1, dir1.d.tail); 5529 if (err) { 5530 goto cleanup; 5531 } 5532 5533 // create new dir and bind as temporary pretend root 5534 err = lfs_dir_alloc(lfs, &dir2); 5535 if (err) { 5536 goto cleanup; 5537 } 5538 5539 dir2.rev = dir1.d.rev; 5540 dir1.head[0] = dir1.pair[0]; 5541 dir1.head[1] = dir1.pair[1]; 5542 lfs->root[0] = dir2.pair[0]; 5543 lfs->root[1] = dir2.pair[1]; 5544 5545 err = lfs_dir_commit(lfs, &dir2, NULL, 0); 5546 if (err) { 5547 goto cleanup; 5548 } 5549 5550 while (true) { 5551 lfs1_entry_t entry1; 5552 err = lfs1_dir_next(lfs, &dir1, &entry1); 5553 if (err && err != LFS_ERR_NOENT) { 5554 goto cleanup; 5555 } 5556 5557 if (err == LFS_ERR_NOENT) { 5558 break; 5559 } 5560 5561 // check that entry has not been moved 5562 if (entry1.d.type & 0x80) { 5563 int moved = lfs1_moved(lfs, &entry1.d.u); 5564 if (moved < 0) { 5565 err = moved; 5566 goto cleanup; 5567 } 5568 5569 if (moved) { 5570 continue; 5571 } 5572 5573 entry1.d.type &= ~0x80; 5574 } 5575 5576 // also fetch name 5577 char name[LFS_NAME_MAX+1]; 5578 memset(name, 0, sizeof(name)); 5579 err = lfs1_bd_read(lfs, dir1.pair[0], 5580 entry1.off + 4+entry1.d.elen+entry1.d.alen, 5581 name, entry1.d.nlen); 5582 if (err) { 5583 goto cleanup; 5584 } 5585 5586 bool isdir = (entry1.d.type == LFS1_TYPE_DIR); 5587 5588 // create entry in new dir 5589 err = lfs_dir_fetch(lfs, &dir2, lfs->root); 5590 if (err) { 5591 goto cleanup; 5592 } 5593 5594 uint16_t id; 5595 err = lfs_dir_find(lfs, &dir2, &(const char*){name}, &id); 5596 if (!(err == LFS_ERR_NOENT && id != 0x3ff)) { 5597 err = (err < 0) ? err : LFS_ERR_EXIST; 5598 goto cleanup; 5599 } 5600 5601 lfs1_entry_tole32(&entry1.d); 5602 err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( 5603 {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL}, 5604 {LFS_MKTAG_IF_ELSE(isdir, 5605 LFS_TYPE_DIR, id, entry1.d.nlen, 5606 LFS_TYPE_REG, id, entry1.d.nlen), 5607 name}, 5608 {LFS_MKTAG_IF_ELSE(isdir, 5609 LFS_TYPE_DIRSTRUCT, id, sizeof(entry1.d.u), 5610 LFS_TYPE_CTZSTRUCT, id, sizeof(entry1.d.u)), 5611 &entry1.d.u})); 5612 lfs1_entry_fromle32(&entry1.d); 5613 if (err) { 5614 goto cleanup; 5615 } 5616 } 5617 5618 if (!lfs_pair_isnull(dir1.d.tail)) { 5619 // find last block and update tail to thread into fs 5620 err = lfs_dir_fetch(lfs, &dir2, lfs->root); 5621 if (err) { 5622 goto cleanup; 5623 } 5624 5625 while (dir2.split) { 5626 err = lfs_dir_fetch(lfs, &dir2, dir2.tail); 5627 if (err) { 5628 goto cleanup; 5629 } 5630 } 5631 5632 lfs_pair_tole32(dir2.pair); 5633 err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( 5634 {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir1.d.tail})); 5635 lfs_pair_fromle32(dir2.pair); 5636 if (err) { 5637 goto cleanup; 5638 } 5639 } 5640 5641 // Copy over first block to thread into fs. Unfortunately 5642 // if this fails there is not much we can do. 5643 LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} " 5644 "-> {0x%"PRIx32", 0x%"PRIx32"}", 5645 lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]); 5646 5647 err = lfs_bd_erase(lfs, dir1.head[1]); 5648 if (err) { 5649 goto cleanup; 5650 } 5651 5652 err = lfs_dir_fetch(lfs, &dir2, lfs->root); 5653 if (err) { 5654 goto cleanup; 5655 } 5656 5657 for (lfs_off_t i = 0; i < dir2.off; i++) { 5658 uint8_t dat; 5659 err = lfs_bd_read(lfs, 5660 NULL, &lfs->rcache, dir2.off, 5661 dir2.pair[0], i, &dat, 1); 5662 if (err) { 5663 goto cleanup; 5664 } 5665 5666 err = lfs_bd_prog(lfs, 5667 &lfs->pcache, &lfs->rcache, true, 5668 dir1.head[1], i, &dat, 1); 5669 if (err) { 5670 goto cleanup; 5671 } 5672 } 5673 5674 err = lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, true); 5675 if (err) { 5676 goto cleanup; 5677 } 5678 } 5679 5680 // Create new superblock. This marks a successful migration! 5681 err = lfs1_dir_fetch(lfs, &dir1, (const lfs_block_t[2]){0, 1}); 5682 if (err) { 5683 goto cleanup; 5684 } 5685 5686 dir2.pair[0] = dir1.pair[0]; 5687 dir2.pair[1] = dir1.pair[1]; 5688 dir2.rev = dir1.d.rev; 5689 dir2.off = sizeof(dir2.rev); 5690 dir2.etag = 0xffffffff; 5691 dir2.count = 0; 5692 dir2.tail[0] = lfs->lfs1->root[0]; 5693 dir2.tail[1] = lfs->lfs1->root[1]; 5694 dir2.erased = false; 5695 dir2.split = true; 5696 5697 lfs_superblock_t superblock = { 5698 .version = LFS_DISK_VERSION, 5699 .block_size = lfs->cfg->block_size, 5700 .block_count = lfs->cfg->block_count, 5701 .name_max = lfs->name_max, 5702 .file_max = lfs->file_max, 5703 .attr_max = lfs->attr_max, 5704 }; 5705 5706 lfs_superblock_tole32(&superblock); 5707 err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS( 5708 {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL}, 5709 {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, 5710 {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)), 5711 &superblock})); 5712 if (err) { 5713 goto cleanup; 5714 } 5715 5716 // sanity check that fetch works 5717 err = lfs_dir_fetch(lfs, &dir2, (const lfs_block_t[2]){0, 1}); 5718 if (err) { 5719 goto cleanup; 5720 } 5721 5722 // force compaction to prevent accidentally mounting v1 5723 dir2.erased = false; 5724 err = lfs_dir_commit(lfs, &dir2, NULL, 0); 5725 if (err) { 5726 goto cleanup; 5727 } 5728 } 5729 5730cleanup: 5731 lfs1_unmount(lfs); 5732 return err; 5733} 5734 5735#endif 5736 5737 5738/// Public API wrappers /// 5739 5740// Here we can add tracing/thread safety easily 5741 5742// Thread-safe wrappers if enabled 5743#ifdef LFS_THREADSAFE 5744#define LFS_LOCK(cfg) cfg->lock(cfg) 5745#define LFS_UNLOCK(cfg) cfg->unlock(cfg) 5746#else 5747#define LFS_LOCK(cfg) ((void)cfg, 0) 5748#define LFS_UNLOCK(cfg) ((void)cfg) 5749#endif 5750 5751// Public API 5752#ifndef LFS_READONLY 5753int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { 5754 int err = LFS_LOCK(cfg); 5755 if (err) { 5756 return err; 5757 } 5758 LFS_TRACE("lfs_format(%p, %p {.context=%p, " 5759 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " 5760 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " 5761 ".block_size=%"PRIu32", .block_count=%"PRIu32", " 5762 ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " 5763 ".lookahead_size=%"PRIu32", .read_buffer=%p, " 5764 ".prog_buffer=%p, .lookahead_buffer=%p, " 5765 ".name_max=%"PRIu32", .file_max=%"PRIu32", " 5766 ".attr_max=%"PRIu32"})", 5767 (void*)lfs, (void*)cfg, cfg->context, 5768 (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 5769 (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 5770 cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, 5771 cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, 5772 cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, 5773 cfg->name_max, cfg->file_max, cfg->attr_max); 5774 5775 err = lfs_rawformat(lfs, cfg); 5776 5777 LFS_TRACE("lfs_format -> %d", err); 5778 LFS_UNLOCK(cfg); 5779 return err; 5780} 5781#endif 5782 5783int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { 5784 int err = LFS_LOCK(cfg); 5785 if (err) { 5786 return err; 5787 } 5788 LFS_TRACE("lfs_mount(%p, %p {.context=%p, " 5789 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " 5790 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " 5791 ".block_size=%"PRIu32", .block_count=%"PRIu32", " 5792 ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " 5793 ".lookahead_size=%"PRIu32", .read_buffer=%p, " 5794 ".prog_buffer=%p, .lookahead_buffer=%p, " 5795 ".name_max=%"PRIu32", .file_max=%"PRIu32", " 5796 ".attr_max=%"PRIu32"})", 5797 (void*)lfs, (void*)cfg, cfg->context, 5798 (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 5799 (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 5800 cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, 5801 cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, 5802 cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, 5803 cfg->name_max, cfg->file_max, cfg->attr_max); 5804 5805 err = lfs_rawmount(lfs, cfg); 5806 5807 LFS_TRACE("lfs_mount -> %d", err); 5808 LFS_UNLOCK(cfg); 5809 return err; 5810} 5811 5812int lfs_unmount(lfs_t *lfs) { 5813 int err = LFS_LOCK(lfs->cfg); 5814 if (err) { 5815 return err; 5816 } 5817 LFS_TRACE("lfs_unmount(%p)", (void*)lfs); 5818 5819 err = lfs_rawunmount(lfs); 5820 5821 LFS_TRACE("lfs_unmount -> %d", err); 5822 LFS_UNLOCK(lfs->cfg); 5823 return err; 5824} 5825 5826#ifndef LFS_READONLY 5827int lfs_remove(lfs_t *lfs, const char *path) { 5828 int err = LFS_LOCK(lfs->cfg); 5829 if (err) { 5830 return err; 5831 } 5832 LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path); 5833 5834 err = lfs_rawremove(lfs, path); 5835 5836 LFS_TRACE("lfs_remove -> %d", err); 5837 LFS_UNLOCK(lfs->cfg); 5838 return err; 5839} 5840#endif 5841 5842#ifndef LFS_READONLY 5843int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { 5844 int err = LFS_LOCK(lfs->cfg); 5845 if (err) { 5846 return err; 5847 } 5848 LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath); 5849 5850 err = lfs_rawrename(lfs, oldpath, newpath); 5851 5852 LFS_TRACE("lfs_rename -> %d", err); 5853 LFS_UNLOCK(lfs->cfg); 5854 return err; 5855} 5856#endif 5857 5858int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { 5859 int err = LFS_LOCK(lfs->cfg); 5860 if (err) { 5861 return err; 5862 } 5863 LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info); 5864 5865 err = lfs_rawstat(lfs, path, info); 5866 5867 LFS_TRACE("lfs_stat -> %d", err); 5868 LFS_UNLOCK(lfs->cfg); 5869 return err; 5870} 5871 5872lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path, 5873 uint8_t type, void *buffer, lfs_size_t size) { 5874 int err = LFS_LOCK(lfs->cfg); 5875 if (err) { 5876 return err; 5877 } 5878 LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", 5879 (void*)lfs, path, type, buffer, size); 5880 5881 lfs_ssize_t res = lfs_rawgetattr(lfs, path, type, buffer, size); 5882 5883 LFS_TRACE("lfs_getattr -> %"PRId32, res); 5884 LFS_UNLOCK(lfs->cfg); 5885 return res; 5886} 5887 5888#ifndef LFS_READONLY 5889int lfs_setattr(lfs_t *lfs, const char *path, 5890 uint8_t type, const void *buffer, lfs_size_t size) { 5891 int err = LFS_LOCK(lfs->cfg); 5892 if (err) { 5893 return err; 5894 } 5895 LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", 5896 (void*)lfs, path, type, buffer, size); 5897 5898 err = lfs_rawsetattr(lfs, path, type, buffer, size); 5899 5900 LFS_TRACE("lfs_setattr -> %d", err); 5901 LFS_UNLOCK(lfs->cfg); 5902 return err; 5903} 5904#endif 5905 5906#ifndef LFS_READONLY 5907int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { 5908 int err = LFS_LOCK(lfs->cfg); 5909 if (err) { 5910 return err; 5911 } 5912 LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type); 5913 5914 err = lfs_rawremoveattr(lfs, path, type); 5915 5916 LFS_TRACE("lfs_removeattr -> %d", err); 5917 LFS_UNLOCK(lfs->cfg); 5918 return err; 5919} 5920#endif 5921 5922#ifndef LFS_NO_MALLOC 5923int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) { 5924 int err = LFS_LOCK(lfs->cfg); 5925 if (err) { 5926 return err; 5927 } 5928 LFS_TRACE("lfs_file_open(%p, %p, \"%s\", %x)", 5929 (void*)lfs, (void*)file, path, flags); 5930 LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 5931 5932 err = lfs_file_rawopen(lfs, file, path, flags); 5933 5934 LFS_TRACE("lfs_file_open -> %d", err); 5935 LFS_UNLOCK(lfs->cfg); 5936 return err; 5937} 5938#endif 5939 5940int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, 5941 const char *path, int flags, 5942 const struct lfs_file_config *cfg) { 5943 int err = LFS_LOCK(lfs->cfg); 5944 if (err) { 5945 return err; 5946 } 5947 LFS_TRACE("lfs_file_opencfg(%p, %p, \"%s\", %x, %p {" 5948 ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", 5949 (void*)lfs, (void*)file, path, flags, 5950 (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); 5951 LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 5952 5953 err = lfs_file_rawopencfg(lfs, file, path, flags, cfg); 5954 5955 LFS_TRACE("lfs_file_opencfg -> %d", err); 5956 LFS_UNLOCK(lfs->cfg); 5957 return err; 5958} 5959 5960int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { 5961 int err = LFS_LOCK(lfs->cfg); 5962 if (err) { 5963 return err; 5964 } 5965 LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file); 5966 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 5967 5968 err = lfs_file_rawclose(lfs, file); 5969 5970 LFS_TRACE("lfs_file_close -> %d", err); 5971 LFS_UNLOCK(lfs->cfg); 5972 return err; 5973} 5974 5975#ifndef LFS_READONLY 5976int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { 5977 int err = LFS_LOCK(lfs->cfg); 5978 if (err) { 5979 return err; 5980 } 5981 LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file); 5982 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 5983 5984 err = lfs_file_rawsync(lfs, file); 5985 5986 LFS_TRACE("lfs_file_sync -> %d", err); 5987 LFS_UNLOCK(lfs->cfg); 5988 return err; 5989} 5990#endif 5991 5992lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, 5993 void *buffer, lfs_size_t size) { 5994 int err = LFS_LOCK(lfs->cfg); 5995 if (err) { 5996 return err; 5997 } 5998 LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")", 5999 (void*)lfs, (void*)file, buffer, size); 6000 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6001 6002 lfs_ssize_t res = lfs_file_rawread(lfs, file, buffer, size); 6003 6004 LFS_TRACE("lfs_file_read -> %"PRId32, res); 6005 LFS_UNLOCK(lfs->cfg); 6006 return res; 6007} 6008 6009#ifndef LFS_READONLY 6010lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, 6011 const void *buffer, lfs_size_t size) { 6012 int err = LFS_LOCK(lfs->cfg); 6013 if (err) { 6014 return err; 6015 } 6016 LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")", 6017 (void*)lfs, (void*)file, buffer, size); 6018 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6019 6020 lfs_ssize_t res = lfs_file_rawwrite(lfs, file, buffer, size); 6021 6022 LFS_TRACE("lfs_file_write -> %"PRId32, res); 6023 LFS_UNLOCK(lfs->cfg); 6024 return res; 6025} 6026#endif 6027 6028lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, 6029 lfs_soff_t off, int whence) { 6030 int err = LFS_LOCK(lfs->cfg); 6031 if (err) { 6032 return err; 6033 } 6034 LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)", 6035 (void*)lfs, (void*)file, off, whence); 6036 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6037 6038 lfs_soff_t res = lfs_file_rawseek(lfs, file, off, whence); 6039 6040 LFS_TRACE("lfs_file_seek -> %"PRId32, res); 6041 LFS_UNLOCK(lfs->cfg); 6042 return res; 6043} 6044 6045#ifndef LFS_READONLY 6046int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { 6047 int err = LFS_LOCK(lfs->cfg); 6048 if (err) { 6049 return err; 6050 } 6051 LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")", 6052 (void*)lfs, (void*)file, size); 6053 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6054 6055 err = lfs_file_rawtruncate(lfs, file, size); 6056 6057 LFS_TRACE("lfs_file_truncate -> %d", err); 6058 LFS_UNLOCK(lfs->cfg); 6059 return err; 6060} 6061#endif 6062 6063lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { 6064 int err = LFS_LOCK(lfs->cfg); 6065 if (err) { 6066 return err; 6067 } 6068 LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file); 6069 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6070 6071 lfs_soff_t res = lfs_file_rawtell(lfs, file); 6072 6073 LFS_TRACE("lfs_file_tell -> %"PRId32, res); 6074 LFS_UNLOCK(lfs->cfg); 6075 return res; 6076} 6077 6078int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { 6079 int err = LFS_LOCK(lfs->cfg); 6080 if (err) { 6081 return err; 6082 } 6083 LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file); 6084 6085 err = lfs_file_rawrewind(lfs, file); 6086 6087 LFS_TRACE("lfs_file_rewind -> %d", err); 6088 LFS_UNLOCK(lfs->cfg); 6089 return err; 6090} 6091 6092lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { 6093 int err = LFS_LOCK(lfs->cfg); 6094 if (err) { 6095 return err; 6096 } 6097 LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file); 6098 LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); 6099 6100 lfs_soff_t res = lfs_file_rawsize(lfs, file); 6101 6102 LFS_TRACE("lfs_file_size -> %"PRId32, res); 6103 LFS_UNLOCK(lfs->cfg); 6104 return res; 6105} 6106 6107#ifndef LFS_READONLY 6108int lfs_mkdir(lfs_t *lfs, const char *path) { 6109 int err = LFS_LOCK(lfs->cfg); 6110 if (err) { 6111 return err; 6112 } 6113 LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path); 6114 6115 err = lfs_rawmkdir(lfs, path); 6116 6117 LFS_TRACE("lfs_mkdir -> %d", err); 6118 LFS_UNLOCK(lfs->cfg); 6119 return err; 6120} 6121#endif 6122 6123int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { 6124 int err = LFS_LOCK(lfs->cfg); 6125 if (err) { 6126 return err; 6127 } 6128 LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path); 6129 LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)dir)); 6130 6131 err = lfs_dir_rawopen(lfs, dir, path); 6132 6133 LFS_TRACE("lfs_dir_open -> %d", err); 6134 LFS_UNLOCK(lfs->cfg); 6135 return err; 6136} 6137 6138int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) { 6139 int err = LFS_LOCK(lfs->cfg); 6140 if (err) { 6141 return err; 6142 } 6143 LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir); 6144 6145 err = lfs_dir_rawclose(lfs, dir); 6146 6147 LFS_TRACE("lfs_dir_close -> %d", err); 6148 LFS_UNLOCK(lfs->cfg); 6149 return err; 6150} 6151 6152int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { 6153 int err = LFS_LOCK(lfs->cfg); 6154 if (err) { 6155 return err; 6156 } 6157 LFS_TRACE("lfs_dir_read(%p, %p, %p)", 6158 (void*)lfs, (void*)dir, (void*)info); 6159 6160 err = lfs_dir_rawread(lfs, dir, info); 6161 6162 LFS_TRACE("lfs_dir_read -> %d", err); 6163 LFS_UNLOCK(lfs->cfg); 6164 return err; 6165} 6166 6167int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) { 6168 int err = LFS_LOCK(lfs->cfg); 6169 if (err) { 6170 return err; 6171 } 6172 LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")", 6173 (void*)lfs, (void*)dir, off); 6174 6175 err = lfs_dir_rawseek(lfs, dir, off); 6176 6177 LFS_TRACE("lfs_dir_seek -> %d", err); 6178 LFS_UNLOCK(lfs->cfg); 6179 return err; 6180} 6181 6182lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) { 6183 int err = LFS_LOCK(lfs->cfg); 6184 if (err) { 6185 return err; 6186 } 6187 LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir); 6188 6189 lfs_soff_t res = lfs_dir_rawtell(lfs, dir); 6190 6191 LFS_TRACE("lfs_dir_tell -> %"PRId32, res); 6192 LFS_UNLOCK(lfs->cfg); 6193 return res; 6194} 6195 6196int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) { 6197 int err = LFS_LOCK(lfs->cfg); 6198 if (err) { 6199 return err; 6200 } 6201 LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir); 6202 6203 err = lfs_dir_rawrewind(lfs, dir); 6204 6205 LFS_TRACE("lfs_dir_rewind -> %d", err); 6206 LFS_UNLOCK(lfs->cfg); 6207 return err; 6208} 6209 6210int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) { 6211 int err = LFS_LOCK(lfs->cfg); 6212 if (err) { 6213 return err; 6214 } 6215 LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo); 6216 6217 err = lfs_fs_rawstat(lfs, fsinfo); 6218 6219 LFS_TRACE("lfs_fs_stat -> %d", err); 6220 LFS_UNLOCK(lfs->cfg); 6221 return err; 6222} 6223 6224lfs_ssize_t lfs_fs_size(lfs_t *lfs) { 6225 int err = LFS_LOCK(lfs->cfg); 6226 if (err) { 6227 return err; 6228 } 6229 LFS_TRACE("lfs_fs_size(%p)", (void*)lfs); 6230 6231 lfs_ssize_t res = lfs_fs_rawsize(lfs); 6232 6233 LFS_TRACE("lfs_fs_size -> %"PRId32, res); 6234 LFS_UNLOCK(lfs->cfg); 6235 return res; 6236} 6237 6238int lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) { 6239 int err = LFS_LOCK(lfs->cfg); 6240 if (err) { 6241 return err; 6242 } 6243 LFS_TRACE("lfs_fs_traverse(%p, %p, %p)", 6244 (void*)lfs, (void*)(uintptr_t)cb, data); 6245 6246 err = lfs_fs_rawtraverse(lfs, cb, data, true); 6247 6248 LFS_TRACE("lfs_fs_traverse -> %d", err); 6249 LFS_UNLOCK(lfs->cfg); 6250 return err; 6251} 6252 6253#ifndef LFS_READONLY 6254int lfs_fs_gc(lfs_t *lfs) { 6255 int err = LFS_LOCK(lfs->cfg); 6256 if (err) { 6257 return err; 6258 } 6259 LFS_TRACE("lfs_fs_gc(%p)", (void*)lfs); 6260 6261 err = lfs_fs_rawgc(lfs); 6262 6263 LFS_TRACE("lfs_fs_gc -> %d", err); 6264 LFS_UNLOCK(lfs->cfg); 6265 return err; 6266} 6267#endif 6268 6269#ifndef LFS_READONLY 6270int lfs_fs_mkconsistent(lfs_t *lfs) { 6271 int err = LFS_LOCK(lfs->cfg); 6272 if (err) { 6273 return err; 6274 } 6275 LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs); 6276 6277 err = lfs_fs_rawmkconsistent(lfs); 6278 6279 LFS_TRACE("lfs_fs_mkconsistent -> %d", err); 6280 LFS_UNLOCK(lfs->cfg); 6281 return err; 6282} 6283#endif 6284 6285#ifndef LFS_READONLY 6286int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) { 6287 int err = LFS_LOCK(lfs->cfg); 6288 if (err) { 6289 return err; 6290 } 6291 LFS_TRACE("lfs_fs_grow(%p, %"PRIu32")", (void*)lfs, block_count); 6292 6293 err = lfs_fs_rawgrow(lfs, block_count); 6294 6295 LFS_TRACE("lfs_fs_grow -> %d", err); 6296 LFS_UNLOCK(lfs->cfg); 6297 return err; 6298} 6299#endif 6300 6301#ifdef LFS_MIGRATE 6302int lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) { 6303 int err = LFS_LOCK(cfg); 6304 if (err) { 6305 return err; 6306 } 6307 LFS_TRACE("lfs_migrate(%p, %p {.context=%p, " 6308 ".read=%p, .prog=%p, .erase=%p, .sync=%p, " 6309 ".read_size=%"PRIu32", .prog_size=%"PRIu32", " 6310 ".block_size=%"PRIu32", .block_count=%"PRIu32", " 6311 ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " 6312 ".lookahead_size=%"PRIu32", .read_buffer=%p, " 6313 ".prog_buffer=%p, .lookahead_buffer=%p, " 6314 ".name_max=%"PRIu32", .file_max=%"PRIu32", " 6315 ".attr_max=%"PRIu32"})", 6316 (void*)lfs, (void*)cfg, cfg->context, 6317 (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, 6318 (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, 6319 cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, 6320 cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, 6321 cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, 6322 cfg->name_max, cfg->file_max, cfg->attr_max); 6323 6324 err = lfs_rawmigrate(lfs, cfg); 6325 6326 LFS_TRACE("lfs_migrate -> %d", err); 6327 LFS_UNLOCK(cfg); 6328 return err; 6329} 6330#endif 6331 6332