xref: /third_party/littlefs/lfs.c (revision 19ea8026)
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