119ea8026Sopenharmony_ci/*
219ea8026Sopenharmony_ci * The little filesystem
319ea8026Sopenharmony_ci *
419ea8026Sopenharmony_ci * Copyright (c) 2022, The littlefs authors.
519ea8026Sopenharmony_ci * Copyright (c) 2017, Arm Limited. All rights reserved.
619ea8026Sopenharmony_ci * SPDX-License-Identifier: BSD-3-Clause
719ea8026Sopenharmony_ci */
819ea8026Sopenharmony_ci#include "lfs.h"
919ea8026Sopenharmony_ci#include "lfs_util.h"
1019ea8026Sopenharmony_ci
1119ea8026Sopenharmony_ci
1219ea8026Sopenharmony_ci// some constants used throughout the code
1319ea8026Sopenharmony_ci#define LFS_BLOCK_NULL ((lfs_block_t)-1)
1419ea8026Sopenharmony_ci#define LFS_BLOCK_INLINE ((lfs_block_t)-2)
1519ea8026Sopenharmony_ci
1619ea8026Sopenharmony_cienum {
1719ea8026Sopenharmony_ci    LFS_OK_RELOCATED = 1,
1819ea8026Sopenharmony_ci    LFS_OK_DROPPED   = 2,
1919ea8026Sopenharmony_ci    LFS_OK_ORPHANED  = 3,
2019ea8026Sopenharmony_ci};
2119ea8026Sopenharmony_ci
2219ea8026Sopenharmony_cienum {
2319ea8026Sopenharmony_ci    LFS_CMP_EQ = 0,
2419ea8026Sopenharmony_ci    LFS_CMP_LT = 1,
2519ea8026Sopenharmony_ci    LFS_CMP_GT = 2,
2619ea8026Sopenharmony_ci};
2719ea8026Sopenharmony_ci
2819ea8026Sopenharmony_ci
2919ea8026Sopenharmony_ci/// Caching block device operations ///
3019ea8026Sopenharmony_ci
3119ea8026Sopenharmony_cistatic inline void lfs_cache_drop(lfs_t *lfs, lfs_cache_t *rcache) {
3219ea8026Sopenharmony_ci    // do not zero, cheaper if cache is readonly or only going to be
3319ea8026Sopenharmony_ci    // written with identical data (during relocates)
3419ea8026Sopenharmony_ci    (void)lfs;
3519ea8026Sopenharmony_ci    rcache->block = LFS_BLOCK_NULL;
3619ea8026Sopenharmony_ci}
3719ea8026Sopenharmony_ci
3819ea8026Sopenharmony_cistatic inline void lfs_cache_zero(lfs_t *lfs, lfs_cache_t *pcache) {
3919ea8026Sopenharmony_ci    // zero to avoid information leak
4019ea8026Sopenharmony_ci    memset(pcache->buffer, 0xff, lfs->cfg->cache_size);
4119ea8026Sopenharmony_ci    pcache->block = LFS_BLOCK_NULL;
4219ea8026Sopenharmony_ci}
4319ea8026Sopenharmony_ci
4419ea8026Sopenharmony_cistatic int lfs_bd_read(lfs_t *lfs,
4519ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
4619ea8026Sopenharmony_ci        lfs_block_t block, lfs_off_t off,
4719ea8026Sopenharmony_ci        void *buffer, lfs_size_t size) {
4819ea8026Sopenharmony_ci    uint8_t *data = buffer;
4919ea8026Sopenharmony_ci    if (off+size > lfs->cfg->block_size
5019ea8026Sopenharmony_ci            || (lfs->block_count && block >= lfs->block_count)) {
5119ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
5219ea8026Sopenharmony_ci    }
5319ea8026Sopenharmony_ci
5419ea8026Sopenharmony_ci    while (size > 0) {
5519ea8026Sopenharmony_ci        lfs_size_t diff = size;
5619ea8026Sopenharmony_ci
5719ea8026Sopenharmony_ci        if (pcache && block == pcache->block &&
5819ea8026Sopenharmony_ci                off < pcache->off + pcache->size) {
5919ea8026Sopenharmony_ci            if (off >= pcache->off) {
6019ea8026Sopenharmony_ci                // is already in pcache?
6119ea8026Sopenharmony_ci                diff = lfs_min(diff, pcache->size - (off-pcache->off));
6219ea8026Sopenharmony_ci                memcpy(data, &pcache->buffer[off-pcache->off], diff);
6319ea8026Sopenharmony_ci
6419ea8026Sopenharmony_ci                data += diff;
6519ea8026Sopenharmony_ci                off += diff;
6619ea8026Sopenharmony_ci                size -= diff;
6719ea8026Sopenharmony_ci                continue;
6819ea8026Sopenharmony_ci            }
6919ea8026Sopenharmony_ci
7019ea8026Sopenharmony_ci            // pcache takes priority
7119ea8026Sopenharmony_ci            diff = lfs_min(diff, pcache->off-off);
7219ea8026Sopenharmony_ci        }
7319ea8026Sopenharmony_ci
7419ea8026Sopenharmony_ci        if (block == rcache->block &&
7519ea8026Sopenharmony_ci                off < rcache->off + rcache->size) {
7619ea8026Sopenharmony_ci            if (off >= rcache->off) {
7719ea8026Sopenharmony_ci                // is already in rcache?
7819ea8026Sopenharmony_ci                diff = lfs_min(diff, rcache->size - (off-rcache->off));
7919ea8026Sopenharmony_ci                memcpy(data, &rcache->buffer[off-rcache->off], diff);
8019ea8026Sopenharmony_ci
8119ea8026Sopenharmony_ci                data += diff;
8219ea8026Sopenharmony_ci                off += diff;
8319ea8026Sopenharmony_ci                size -= diff;
8419ea8026Sopenharmony_ci                continue;
8519ea8026Sopenharmony_ci            }
8619ea8026Sopenharmony_ci
8719ea8026Sopenharmony_ci            // rcache takes priority
8819ea8026Sopenharmony_ci            diff = lfs_min(diff, rcache->off-off);
8919ea8026Sopenharmony_ci        }
9019ea8026Sopenharmony_ci
9119ea8026Sopenharmony_ci        if (size >= hint && off % lfs->cfg->read_size == 0 &&
9219ea8026Sopenharmony_ci                size >= lfs->cfg->read_size) {
9319ea8026Sopenharmony_ci            // bypass cache?
9419ea8026Sopenharmony_ci            diff = lfs_aligndown(diff, lfs->cfg->read_size);
9519ea8026Sopenharmony_ci            int err = lfs->cfg->read(lfs->cfg, block, off, data, diff);
9619ea8026Sopenharmony_ci            if (err) {
9719ea8026Sopenharmony_ci                return err;
9819ea8026Sopenharmony_ci            }
9919ea8026Sopenharmony_ci
10019ea8026Sopenharmony_ci            data += diff;
10119ea8026Sopenharmony_ci            off += diff;
10219ea8026Sopenharmony_ci            size -= diff;
10319ea8026Sopenharmony_ci            continue;
10419ea8026Sopenharmony_ci        }
10519ea8026Sopenharmony_ci
10619ea8026Sopenharmony_ci        // load to cache, first condition can no longer fail
10719ea8026Sopenharmony_ci        LFS_ASSERT(!lfs->block_count || block < lfs->block_count);
10819ea8026Sopenharmony_ci        rcache->block = block;
10919ea8026Sopenharmony_ci        rcache->off = lfs_aligndown(off, lfs->cfg->read_size);
11019ea8026Sopenharmony_ci        rcache->size = lfs_min(
11119ea8026Sopenharmony_ci                lfs_min(
11219ea8026Sopenharmony_ci                    lfs_alignup(off+hint, lfs->cfg->read_size),
11319ea8026Sopenharmony_ci                    lfs->cfg->block_size)
11419ea8026Sopenharmony_ci                - rcache->off,
11519ea8026Sopenharmony_ci                lfs->cfg->cache_size);
11619ea8026Sopenharmony_ci        int err = lfs->cfg->read(lfs->cfg, rcache->block,
11719ea8026Sopenharmony_ci                rcache->off, rcache->buffer, rcache->size);
11819ea8026Sopenharmony_ci        LFS_ASSERT(err <= 0);
11919ea8026Sopenharmony_ci        if (err) {
12019ea8026Sopenharmony_ci            return err;
12119ea8026Sopenharmony_ci        }
12219ea8026Sopenharmony_ci    }
12319ea8026Sopenharmony_ci
12419ea8026Sopenharmony_ci    return 0;
12519ea8026Sopenharmony_ci}
12619ea8026Sopenharmony_ci
12719ea8026Sopenharmony_cistatic int lfs_bd_cmp(lfs_t *lfs,
12819ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
12919ea8026Sopenharmony_ci        lfs_block_t block, lfs_off_t off,
13019ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
13119ea8026Sopenharmony_ci    const uint8_t *data = buffer;
13219ea8026Sopenharmony_ci    lfs_size_t diff = 0;
13319ea8026Sopenharmony_ci
13419ea8026Sopenharmony_ci    for (lfs_off_t i = 0; i < size; i += diff) {
13519ea8026Sopenharmony_ci        uint8_t dat[8];
13619ea8026Sopenharmony_ci
13719ea8026Sopenharmony_ci        diff = lfs_min(size-i, sizeof(dat));
13819ea8026Sopenharmony_ci        int err = lfs_bd_read(lfs,
13919ea8026Sopenharmony_ci                pcache, rcache, hint-i,
14019ea8026Sopenharmony_ci                block, off+i, &dat, diff);
14119ea8026Sopenharmony_ci        if (err) {
14219ea8026Sopenharmony_ci            return err;
14319ea8026Sopenharmony_ci        }
14419ea8026Sopenharmony_ci
14519ea8026Sopenharmony_ci        int res = memcmp(dat, data + i, diff);
14619ea8026Sopenharmony_ci        if (res) {
14719ea8026Sopenharmony_ci            return res < 0 ? LFS_CMP_LT : LFS_CMP_GT;
14819ea8026Sopenharmony_ci        }
14919ea8026Sopenharmony_ci    }
15019ea8026Sopenharmony_ci
15119ea8026Sopenharmony_ci    return LFS_CMP_EQ;
15219ea8026Sopenharmony_ci}
15319ea8026Sopenharmony_ci
15419ea8026Sopenharmony_cistatic int lfs_bd_crc(lfs_t *lfs,
15519ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
15619ea8026Sopenharmony_ci        lfs_block_t block, lfs_off_t off, lfs_size_t size, uint32_t *crc) {
15719ea8026Sopenharmony_ci    lfs_size_t diff = 0;
15819ea8026Sopenharmony_ci
15919ea8026Sopenharmony_ci    for (lfs_off_t i = 0; i < size; i += diff) {
16019ea8026Sopenharmony_ci        uint8_t dat[8];
16119ea8026Sopenharmony_ci        diff = lfs_min(size-i, sizeof(dat));
16219ea8026Sopenharmony_ci        int err = lfs_bd_read(lfs,
16319ea8026Sopenharmony_ci                pcache, rcache, hint-i,
16419ea8026Sopenharmony_ci                block, off+i, &dat, diff);
16519ea8026Sopenharmony_ci        if (err) {
16619ea8026Sopenharmony_ci            return err;
16719ea8026Sopenharmony_ci        }
16819ea8026Sopenharmony_ci
16919ea8026Sopenharmony_ci        *crc = lfs_crc(*crc, &dat, diff);
17019ea8026Sopenharmony_ci    }
17119ea8026Sopenharmony_ci
17219ea8026Sopenharmony_ci    return 0;
17319ea8026Sopenharmony_ci}
17419ea8026Sopenharmony_ci
17519ea8026Sopenharmony_ci#ifndef LFS_READONLY
17619ea8026Sopenharmony_cistatic int lfs_bd_flush(lfs_t *lfs,
17719ea8026Sopenharmony_ci        lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
17819ea8026Sopenharmony_ci    if (pcache->block != LFS_BLOCK_NULL && pcache->block != LFS_BLOCK_INLINE) {
17919ea8026Sopenharmony_ci        LFS_ASSERT(pcache->block < lfs->block_count);
18019ea8026Sopenharmony_ci        lfs_size_t diff = lfs_alignup(pcache->size, lfs->cfg->prog_size);
18119ea8026Sopenharmony_ci        int err = lfs->cfg->prog(lfs->cfg, pcache->block,
18219ea8026Sopenharmony_ci                pcache->off, pcache->buffer, diff);
18319ea8026Sopenharmony_ci        LFS_ASSERT(err <= 0);
18419ea8026Sopenharmony_ci        if (err) {
18519ea8026Sopenharmony_ci            return err;
18619ea8026Sopenharmony_ci        }
18719ea8026Sopenharmony_ci
18819ea8026Sopenharmony_ci        if (validate) {
18919ea8026Sopenharmony_ci            // check data on disk
19019ea8026Sopenharmony_ci            lfs_cache_drop(lfs, rcache);
19119ea8026Sopenharmony_ci            int res = lfs_bd_cmp(lfs,
19219ea8026Sopenharmony_ci                    NULL, rcache, diff,
19319ea8026Sopenharmony_ci                    pcache->block, pcache->off, pcache->buffer, diff);
19419ea8026Sopenharmony_ci            if (res < 0) {
19519ea8026Sopenharmony_ci                return res;
19619ea8026Sopenharmony_ci            }
19719ea8026Sopenharmony_ci
19819ea8026Sopenharmony_ci            if (res != LFS_CMP_EQ) {
19919ea8026Sopenharmony_ci                return LFS_ERR_CORRUPT;
20019ea8026Sopenharmony_ci            }
20119ea8026Sopenharmony_ci        }
20219ea8026Sopenharmony_ci
20319ea8026Sopenharmony_ci        lfs_cache_zero(lfs, pcache);
20419ea8026Sopenharmony_ci    }
20519ea8026Sopenharmony_ci
20619ea8026Sopenharmony_ci    return 0;
20719ea8026Sopenharmony_ci}
20819ea8026Sopenharmony_ci#endif
20919ea8026Sopenharmony_ci
21019ea8026Sopenharmony_ci#ifndef LFS_READONLY
21119ea8026Sopenharmony_cistatic int lfs_bd_sync(lfs_t *lfs,
21219ea8026Sopenharmony_ci        lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate) {
21319ea8026Sopenharmony_ci    lfs_cache_drop(lfs, rcache);
21419ea8026Sopenharmony_ci
21519ea8026Sopenharmony_ci    int err = lfs_bd_flush(lfs, pcache, rcache, validate);
21619ea8026Sopenharmony_ci    if (err) {
21719ea8026Sopenharmony_ci        return err;
21819ea8026Sopenharmony_ci    }
21919ea8026Sopenharmony_ci
22019ea8026Sopenharmony_ci    err = lfs->cfg->sync(lfs->cfg);
22119ea8026Sopenharmony_ci    LFS_ASSERT(err <= 0);
22219ea8026Sopenharmony_ci    return err;
22319ea8026Sopenharmony_ci}
22419ea8026Sopenharmony_ci#endif
22519ea8026Sopenharmony_ci
22619ea8026Sopenharmony_ci#ifndef LFS_READONLY
22719ea8026Sopenharmony_cistatic int lfs_bd_prog(lfs_t *lfs,
22819ea8026Sopenharmony_ci        lfs_cache_t *pcache, lfs_cache_t *rcache, bool validate,
22919ea8026Sopenharmony_ci        lfs_block_t block, lfs_off_t off,
23019ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
23119ea8026Sopenharmony_ci    const uint8_t *data = buffer;
23219ea8026Sopenharmony_ci    LFS_ASSERT(block == LFS_BLOCK_INLINE || block < lfs->block_count);
23319ea8026Sopenharmony_ci    LFS_ASSERT(off + size <= lfs->cfg->block_size);
23419ea8026Sopenharmony_ci
23519ea8026Sopenharmony_ci    while (size > 0) {
23619ea8026Sopenharmony_ci        if (block == pcache->block &&
23719ea8026Sopenharmony_ci                off >= pcache->off &&
23819ea8026Sopenharmony_ci                off < pcache->off + lfs->cfg->cache_size) {
23919ea8026Sopenharmony_ci            // already fits in pcache?
24019ea8026Sopenharmony_ci            lfs_size_t diff = lfs_min(size,
24119ea8026Sopenharmony_ci                    lfs->cfg->cache_size - (off-pcache->off));
24219ea8026Sopenharmony_ci            memcpy(&pcache->buffer[off-pcache->off], data, diff);
24319ea8026Sopenharmony_ci
24419ea8026Sopenharmony_ci            data += diff;
24519ea8026Sopenharmony_ci            off += diff;
24619ea8026Sopenharmony_ci            size -= diff;
24719ea8026Sopenharmony_ci
24819ea8026Sopenharmony_ci            pcache->size = lfs_max(pcache->size, off - pcache->off);
24919ea8026Sopenharmony_ci            if (pcache->size == lfs->cfg->cache_size) {
25019ea8026Sopenharmony_ci                // eagerly flush out pcache if we fill up
25119ea8026Sopenharmony_ci                int err = lfs_bd_flush(lfs, pcache, rcache, validate);
25219ea8026Sopenharmony_ci                if (err) {
25319ea8026Sopenharmony_ci                    return err;
25419ea8026Sopenharmony_ci                }
25519ea8026Sopenharmony_ci            }
25619ea8026Sopenharmony_ci
25719ea8026Sopenharmony_ci            continue;
25819ea8026Sopenharmony_ci        }
25919ea8026Sopenharmony_ci
26019ea8026Sopenharmony_ci        // pcache must have been flushed, either by programming and
26119ea8026Sopenharmony_ci        // entire block or manually flushing the pcache
26219ea8026Sopenharmony_ci        LFS_ASSERT(pcache->block == LFS_BLOCK_NULL);
26319ea8026Sopenharmony_ci
26419ea8026Sopenharmony_ci        // prepare pcache, first condition can no longer fail
26519ea8026Sopenharmony_ci        pcache->block = block;
26619ea8026Sopenharmony_ci        pcache->off = lfs_aligndown(off, lfs->cfg->prog_size);
26719ea8026Sopenharmony_ci        pcache->size = 0;
26819ea8026Sopenharmony_ci    }
26919ea8026Sopenharmony_ci
27019ea8026Sopenharmony_ci    return 0;
27119ea8026Sopenharmony_ci}
27219ea8026Sopenharmony_ci#endif
27319ea8026Sopenharmony_ci
27419ea8026Sopenharmony_ci#ifndef LFS_READONLY
27519ea8026Sopenharmony_cistatic int lfs_bd_erase(lfs_t *lfs, lfs_block_t block) {
27619ea8026Sopenharmony_ci    LFS_ASSERT(block < lfs->block_count);
27719ea8026Sopenharmony_ci    int err = lfs->cfg->erase(lfs->cfg, block);
27819ea8026Sopenharmony_ci    LFS_ASSERT(err <= 0);
27919ea8026Sopenharmony_ci    return err;
28019ea8026Sopenharmony_ci}
28119ea8026Sopenharmony_ci#endif
28219ea8026Sopenharmony_ci
28319ea8026Sopenharmony_ci
28419ea8026Sopenharmony_ci/// Small type-level utilities ///
28519ea8026Sopenharmony_ci// operations on block pairs
28619ea8026Sopenharmony_cistatic inline void lfs_pair_swap(lfs_block_t pair[2]) {
28719ea8026Sopenharmony_ci    lfs_block_t t = pair[0];
28819ea8026Sopenharmony_ci    pair[0] = pair[1];
28919ea8026Sopenharmony_ci    pair[1] = t;
29019ea8026Sopenharmony_ci}
29119ea8026Sopenharmony_ci
29219ea8026Sopenharmony_cistatic inline bool lfs_pair_isnull(const lfs_block_t pair[2]) {
29319ea8026Sopenharmony_ci    return pair[0] == LFS_BLOCK_NULL || pair[1] == LFS_BLOCK_NULL;
29419ea8026Sopenharmony_ci}
29519ea8026Sopenharmony_ci
29619ea8026Sopenharmony_cistatic inline int lfs_pair_cmp(
29719ea8026Sopenharmony_ci        const lfs_block_t paira[2],
29819ea8026Sopenharmony_ci        const lfs_block_t pairb[2]) {
29919ea8026Sopenharmony_ci    return !(paira[0] == pairb[0] || paira[1] == pairb[1] ||
30019ea8026Sopenharmony_ci             paira[0] == pairb[1] || paira[1] == pairb[0]);
30119ea8026Sopenharmony_ci}
30219ea8026Sopenharmony_ci
30319ea8026Sopenharmony_cistatic inline bool lfs_pair_issync(
30419ea8026Sopenharmony_ci        const lfs_block_t paira[2],
30519ea8026Sopenharmony_ci        const lfs_block_t pairb[2]) {
30619ea8026Sopenharmony_ci    return (paira[0] == pairb[0] && paira[1] == pairb[1]) ||
30719ea8026Sopenharmony_ci           (paira[0] == pairb[1] && paira[1] == pairb[0]);
30819ea8026Sopenharmony_ci}
30919ea8026Sopenharmony_ci
31019ea8026Sopenharmony_cistatic inline void lfs_pair_fromle32(lfs_block_t pair[2]) {
31119ea8026Sopenharmony_ci    pair[0] = lfs_fromle32(pair[0]);
31219ea8026Sopenharmony_ci    pair[1] = lfs_fromle32(pair[1]);
31319ea8026Sopenharmony_ci}
31419ea8026Sopenharmony_ci
31519ea8026Sopenharmony_ci#ifndef LFS_READONLY
31619ea8026Sopenharmony_cistatic inline void lfs_pair_tole32(lfs_block_t pair[2]) {
31719ea8026Sopenharmony_ci    pair[0] = lfs_tole32(pair[0]);
31819ea8026Sopenharmony_ci    pair[1] = lfs_tole32(pair[1]);
31919ea8026Sopenharmony_ci}
32019ea8026Sopenharmony_ci#endif
32119ea8026Sopenharmony_ci
32219ea8026Sopenharmony_ci// operations on 32-bit entry tags
32319ea8026Sopenharmony_citypedef uint32_t lfs_tag_t;
32419ea8026Sopenharmony_citypedef int32_t lfs_stag_t;
32519ea8026Sopenharmony_ci
32619ea8026Sopenharmony_ci#define LFS_MKTAG(type, id, size) \
32719ea8026Sopenharmony_ci    (((lfs_tag_t)(type) << 20) | ((lfs_tag_t)(id) << 10) | (lfs_tag_t)(size))
32819ea8026Sopenharmony_ci
32919ea8026Sopenharmony_ci#define LFS_MKTAG_IF(cond, type, id, size) \
33019ea8026Sopenharmony_ci    ((cond) ? LFS_MKTAG(type, id, size) : LFS_MKTAG(LFS_FROM_NOOP, 0, 0))
33119ea8026Sopenharmony_ci
33219ea8026Sopenharmony_ci#define LFS_MKTAG_IF_ELSE(cond, type1, id1, size1, type2, id2, size2) \
33319ea8026Sopenharmony_ci    ((cond) ? LFS_MKTAG(type1, id1, size1) : LFS_MKTAG(type2, id2, size2))
33419ea8026Sopenharmony_ci
33519ea8026Sopenharmony_cistatic inline bool lfs_tag_isvalid(lfs_tag_t tag) {
33619ea8026Sopenharmony_ci    return !(tag & 0x80000000);
33719ea8026Sopenharmony_ci}
33819ea8026Sopenharmony_ci
33919ea8026Sopenharmony_cistatic inline bool lfs_tag_isdelete(lfs_tag_t tag) {
34019ea8026Sopenharmony_ci    return ((int32_t)(tag << 22) >> 22) == -1;
34119ea8026Sopenharmony_ci}
34219ea8026Sopenharmony_ci
34319ea8026Sopenharmony_cistatic inline uint16_t lfs_tag_type1(lfs_tag_t tag) {
34419ea8026Sopenharmony_ci    return (tag & 0x70000000) >> 20;
34519ea8026Sopenharmony_ci}
34619ea8026Sopenharmony_ci
34719ea8026Sopenharmony_cistatic inline uint16_t lfs_tag_type2(lfs_tag_t tag) {
34819ea8026Sopenharmony_ci    return (tag & 0x78000000) >> 20;
34919ea8026Sopenharmony_ci}
35019ea8026Sopenharmony_ci
35119ea8026Sopenharmony_cistatic inline uint16_t lfs_tag_type3(lfs_tag_t tag) {
35219ea8026Sopenharmony_ci    return (tag & 0x7ff00000) >> 20;
35319ea8026Sopenharmony_ci}
35419ea8026Sopenharmony_ci
35519ea8026Sopenharmony_cistatic inline uint8_t lfs_tag_chunk(lfs_tag_t tag) {
35619ea8026Sopenharmony_ci    return (tag & 0x0ff00000) >> 20;
35719ea8026Sopenharmony_ci}
35819ea8026Sopenharmony_ci
35919ea8026Sopenharmony_cistatic inline int8_t lfs_tag_splice(lfs_tag_t tag) {
36019ea8026Sopenharmony_ci    return (int8_t)lfs_tag_chunk(tag);
36119ea8026Sopenharmony_ci}
36219ea8026Sopenharmony_ci
36319ea8026Sopenharmony_cistatic inline uint16_t lfs_tag_id(lfs_tag_t tag) {
36419ea8026Sopenharmony_ci    return (tag & 0x000ffc00) >> 10;
36519ea8026Sopenharmony_ci}
36619ea8026Sopenharmony_ci
36719ea8026Sopenharmony_cistatic inline lfs_size_t lfs_tag_size(lfs_tag_t tag) {
36819ea8026Sopenharmony_ci    return tag & 0x000003ff;
36919ea8026Sopenharmony_ci}
37019ea8026Sopenharmony_ci
37119ea8026Sopenharmony_cistatic inline lfs_size_t lfs_tag_dsize(lfs_tag_t tag) {
37219ea8026Sopenharmony_ci    return sizeof(tag) + lfs_tag_size(tag + lfs_tag_isdelete(tag));
37319ea8026Sopenharmony_ci}
37419ea8026Sopenharmony_ci
37519ea8026Sopenharmony_ci// operations on attributes in attribute lists
37619ea8026Sopenharmony_cistruct lfs_mattr {
37719ea8026Sopenharmony_ci    lfs_tag_t tag;
37819ea8026Sopenharmony_ci    const void *buffer;
37919ea8026Sopenharmony_ci};
38019ea8026Sopenharmony_ci
38119ea8026Sopenharmony_cistruct lfs_diskoff {
38219ea8026Sopenharmony_ci    lfs_block_t block;
38319ea8026Sopenharmony_ci    lfs_off_t off;
38419ea8026Sopenharmony_ci};
38519ea8026Sopenharmony_ci
38619ea8026Sopenharmony_ci#define LFS_MKATTRS(...) \
38719ea8026Sopenharmony_ci    (struct lfs_mattr[]){__VA_ARGS__}, \
38819ea8026Sopenharmony_ci    sizeof((struct lfs_mattr[]){__VA_ARGS__}) / sizeof(struct lfs_mattr)
38919ea8026Sopenharmony_ci
39019ea8026Sopenharmony_ci// operations on global state
39119ea8026Sopenharmony_cistatic inline void lfs_gstate_xor(lfs_gstate_t *a, const lfs_gstate_t *b) {
39219ea8026Sopenharmony_ci    for (int i = 0; i < 3; i++) {
39319ea8026Sopenharmony_ci        ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i];
39419ea8026Sopenharmony_ci    }
39519ea8026Sopenharmony_ci}
39619ea8026Sopenharmony_ci
39719ea8026Sopenharmony_cistatic inline bool lfs_gstate_iszero(const lfs_gstate_t *a) {
39819ea8026Sopenharmony_ci    for (int i = 0; i < 3; i++) {
39919ea8026Sopenharmony_ci        if (((uint32_t*)a)[i] != 0) {
40019ea8026Sopenharmony_ci            return false;
40119ea8026Sopenharmony_ci        }
40219ea8026Sopenharmony_ci    }
40319ea8026Sopenharmony_ci    return true;
40419ea8026Sopenharmony_ci}
40519ea8026Sopenharmony_ci
40619ea8026Sopenharmony_ci#ifndef LFS_READONLY
40719ea8026Sopenharmony_cistatic inline bool lfs_gstate_hasorphans(const lfs_gstate_t *a) {
40819ea8026Sopenharmony_ci    return lfs_tag_size(a->tag);
40919ea8026Sopenharmony_ci}
41019ea8026Sopenharmony_ci
41119ea8026Sopenharmony_cistatic inline uint8_t lfs_gstate_getorphans(const lfs_gstate_t *a) {
41219ea8026Sopenharmony_ci    return lfs_tag_size(a->tag) & 0x1ff;
41319ea8026Sopenharmony_ci}
41419ea8026Sopenharmony_ci
41519ea8026Sopenharmony_cistatic inline bool lfs_gstate_hasmove(const lfs_gstate_t *a) {
41619ea8026Sopenharmony_ci    return lfs_tag_type1(a->tag);
41719ea8026Sopenharmony_ci}
41819ea8026Sopenharmony_ci#endif
41919ea8026Sopenharmony_ci
42019ea8026Sopenharmony_cistatic inline bool lfs_gstate_needssuperblock(const lfs_gstate_t *a) {
42119ea8026Sopenharmony_ci    return lfs_tag_size(a->tag) >> 9;
42219ea8026Sopenharmony_ci}
42319ea8026Sopenharmony_ci
42419ea8026Sopenharmony_cistatic inline bool lfs_gstate_hasmovehere(const lfs_gstate_t *a,
42519ea8026Sopenharmony_ci        const lfs_block_t *pair) {
42619ea8026Sopenharmony_ci    return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0;
42719ea8026Sopenharmony_ci}
42819ea8026Sopenharmony_ci
42919ea8026Sopenharmony_cistatic inline void lfs_gstate_fromle32(lfs_gstate_t *a) {
43019ea8026Sopenharmony_ci    a->tag     = lfs_fromle32(a->tag);
43119ea8026Sopenharmony_ci    a->pair[0] = lfs_fromle32(a->pair[0]);
43219ea8026Sopenharmony_ci    a->pair[1] = lfs_fromle32(a->pair[1]);
43319ea8026Sopenharmony_ci}
43419ea8026Sopenharmony_ci
43519ea8026Sopenharmony_ci#ifndef LFS_READONLY
43619ea8026Sopenharmony_cistatic inline void lfs_gstate_tole32(lfs_gstate_t *a) {
43719ea8026Sopenharmony_ci    a->tag     = lfs_tole32(a->tag);
43819ea8026Sopenharmony_ci    a->pair[0] = lfs_tole32(a->pair[0]);
43919ea8026Sopenharmony_ci    a->pair[1] = lfs_tole32(a->pair[1]);
44019ea8026Sopenharmony_ci}
44119ea8026Sopenharmony_ci#endif
44219ea8026Sopenharmony_ci
44319ea8026Sopenharmony_ci// operations on forward-CRCs used to track erased state
44419ea8026Sopenharmony_cistruct lfs_fcrc {
44519ea8026Sopenharmony_ci    lfs_size_t size;
44619ea8026Sopenharmony_ci    uint32_t crc;
44719ea8026Sopenharmony_ci};
44819ea8026Sopenharmony_ci
44919ea8026Sopenharmony_cistatic void lfs_fcrc_fromle32(struct lfs_fcrc *fcrc) {
45019ea8026Sopenharmony_ci    fcrc->size = lfs_fromle32(fcrc->size);
45119ea8026Sopenharmony_ci    fcrc->crc = lfs_fromle32(fcrc->crc);
45219ea8026Sopenharmony_ci}
45319ea8026Sopenharmony_ci
45419ea8026Sopenharmony_ci#ifndef LFS_READONLY
45519ea8026Sopenharmony_cistatic void lfs_fcrc_tole32(struct lfs_fcrc *fcrc) {
45619ea8026Sopenharmony_ci    fcrc->size = lfs_tole32(fcrc->size);
45719ea8026Sopenharmony_ci    fcrc->crc = lfs_tole32(fcrc->crc);
45819ea8026Sopenharmony_ci}
45919ea8026Sopenharmony_ci#endif
46019ea8026Sopenharmony_ci
46119ea8026Sopenharmony_ci// other endianness operations
46219ea8026Sopenharmony_cistatic void lfs_ctz_fromle32(struct lfs_ctz *ctz) {
46319ea8026Sopenharmony_ci    ctz->head = lfs_fromle32(ctz->head);
46419ea8026Sopenharmony_ci    ctz->size = lfs_fromle32(ctz->size);
46519ea8026Sopenharmony_ci}
46619ea8026Sopenharmony_ci
46719ea8026Sopenharmony_ci#ifndef LFS_READONLY
46819ea8026Sopenharmony_cistatic void lfs_ctz_tole32(struct lfs_ctz *ctz) {
46919ea8026Sopenharmony_ci    ctz->head = lfs_tole32(ctz->head);
47019ea8026Sopenharmony_ci    ctz->size = lfs_tole32(ctz->size);
47119ea8026Sopenharmony_ci}
47219ea8026Sopenharmony_ci#endif
47319ea8026Sopenharmony_ci
47419ea8026Sopenharmony_cistatic inline void lfs_superblock_fromle32(lfs_superblock_t *superblock) {
47519ea8026Sopenharmony_ci    superblock->version     = lfs_fromle32(superblock->version);
47619ea8026Sopenharmony_ci    superblock->block_size  = lfs_fromle32(superblock->block_size);
47719ea8026Sopenharmony_ci    superblock->block_count = lfs_fromle32(superblock->block_count);
47819ea8026Sopenharmony_ci    superblock->name_max    = lfs_fromle32(superblock->name_max);
47919ea8026Sopenharmony_ci    superblock->file_max    = lfs_fromle32(superblock->file_max);
48019ea8026Sopenharmony_ci    superblock->attr_max    = lfs_fromle32(superblock->attr_max);
48119ea8026Sopenharmony_ci}
48219ea8026Sopenharmony_ci
48319ea8026Sopenharmony_ci#ifndef LFS_READONLY
48419ea8026Sopenharmony_cistatic inline void lfs_superblock_tole32(lfs_superblock_t *superblock) {
48519ea8026Sopenharmony_ci    superblock->version     = lfs_tole32(superblock->version);
48619ea8026Sopenharmony_ci    superblock->block_size  = lfs_tole32(superblock->block_size);
48719ea8026Sopenharmony_ci    superblock->block_count = lfs_tole32(superblock->block_count);
48819ea8026Sopenharmony_ci    superblock->name_max    = lfs_tole32(superblock->name_max);
48919ea8026Sopenharmony_ci    superblock->file_max    = lfs_tole32(superblock->file_max);
49019ea8026Sopenharmony_ci    superblock->attr_max    = lfs_tole32(superblock->attr_max);
49119ea8026Sopenharmony_ci}
49219ea8026Sopenharmony_ci#endif
49319ea8026Sopenharmony_ci
49419ea8026Sopenharmony_ci#ifndef LFS_NO_ASSERT
49519ea8026Sopenharmony_cistatic bool lfs_mlist_isopen(struct lfs_mlist *head,
49619ea8026Sopenharmony_ci        struct lfs_mlist *node) {
49719ea8026Sopenharmony_ci    for (struct lfs_mlist **p = &head; *p; p = &(*p)->next) {
49819ea8026Sopenharmony_ci        if (*p == (struct lfs_mlist*)node) {
49919ea8026Sopenharmony_ci            return true;
50019ea8026Sopenharmony_ci        }
50119ea8026Sopenharmony_ci    }
50219ea8026Sopenharmony_ci
50319ea8026Sopenharmony_ci    return false;
50419ea8026Sopenharmony_ci}
50519ea8026Sopenharmony_ci#endif
50619ea8026Sopenharmony_ci
50719ea8026Sopenharmony_cistatic void lfs_mlist_remove(lfs_t *lfs, struct lfs_mlist *mlist) {
50819ea8026Sopenharmony_ci    for (struct lfs_mlist **p = &lfs->mlist; *p; p = &(*p)->next) {
50919ea8026Sopenharmony_ci        if (*p == mlist) {
51019ea8026Sopenharmony_ci            *p = (*p)->next;
51119ea8026Sopenharmony_ci            break;
51219ea8026Sopenharmony_ci        }
51319ea8026Sopenharmony_ci    }
51419ea8026Sopenharmony_ci}
51519ea8026Sopenharmony_ci
51619ea8026Sopenharmony_cistatic void lfs_mlist_append(lfs_t *lfs, struct lfs_mlist *mlist) {
51719ea8026Sopenharmony_ci    mlist->next = lfs->mlist;
51819ea8026Sopenharmony_ci    lfs->mlist = mlist;
51919ea8026Sopenharmony_ci}
52019ea8026Sopenharmony_ci
52119ea8026Sopenharmony_ci// some other filesystem operations
52219ea8026Sopenharmony_cistatic uint32_t lfs_fs_disk_version(lfs_t *lfs) {
52319ea8026Sopenharmony_ci    (void)lfs;
52419ea8026Sopenharmony_ci#ifdef LFS_MULTIVERSION
52519ea8026Sopenharmony_ci    if (lfs->cfg->disk_version) {
52619ea8026Sopenharmony_ci        return lfs->cfg->disk_version;
52719ea8026Sopenharmony_ci    } else
52819ea8026Sopenharmony_ci#endif
52919ea8026Sopenharmony_ci    {
53019ea8026Sopenharmony_ci        return LFS_DISK_VERSION;
53119ea8026Sopenharmony_ci    }
53219ea8026Sopenharmony_ci}
53319ea8026Sopenharmony_ci
53419ea8026Sopenharmony_cistatic uint16_t lfs_fs_disk_version_major(lfs_t *lfs) {
53519ea8026Sopenharmony_ci    return 0xffff & (lfs_fs_disk_version(lfs) >> 16);
53619ea8026Sopenharmony_ci
53719ea8026Sopenharmony_ci}
53819ea8026Sopenharmony_ci
53919ea8026Sopenharmony_cistatic uint16_t lfs_fs_disk_version_minor(lfs_t *lfs) {
54019ea8026Sopenharmony_ci    return 0xffff & (lfs_fs_disk_version(lfs) >> 0);
54119ea8026Sopenharmony_ci}
54219ea8026Sopenharmony_ci
54319ea8026Sopenharmony_ci
54419ea8026Sopenharmony_ci/// Internal operations predeclared here ///
54519ea8026Sopenharmony_ci#ifndef LFS_READONLY
54619ea8026Sopenharmony_cistatic int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
54719ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount);
54819ea8026Sopenharmony_cistatic int lfs_dir_compact(lfs_t *lfs,
54919ea8026Sopenharmony_ci        lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
55019ea8026Sopenharmony_ci        lfs_mdir_t *source, uint16_t begin, uint16_t end);
55119ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file,
55219ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size);
55319ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file,
55419ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size);
55519ea8026Sopenharmony_cistatic int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file);
55619ea8026Sopenharmony_cistatic int lfs_file_outline(lfs_t *lfs, lfs_file_t *file);
55719ea8026Sopenharmony_cistatic int lfs_file_flush(lfs_t *lfs, lfs_file_t *file);
55819ea8026Sopenharmony_ci
55919ea8026Sopenharmony_cistatic int lfs_fs_deorphan(lfs_t *lfs, bool powerloss);
56019ea8026Sopenharmony_cistatic int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans);
56119ea8026Sopenharmony_cistatic void lfs_fs_prepmove(lfs_t *lfs,
56219ea8026Sopenharmony_ci        uint16_t id, const lfs_block_t pair[2]);
56319ea8026Sopenharmony_cistatic int lfs_fs_pred(lfs_t *lfs, const lfs_block_t dir[2],
56419ea8026Sopenharmony_ci        lfs_mdir_t *pdir);
56519ea8026Sopenharmony_cistatic lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2],
56619ea8026Sopenharmony_ci        lfs_mdir_t *parent);
56719ea8026Sopenharmony_cistatic int lfs_fs_forceconsistency(lfs_t *lfs);
56819ea8026Sopenharmony_ci#endif
56919ea8026Sopenharmony_ci
57019ea8026Sopenharmony_cistatic void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock);
57119ea8026Sopenharmony_ci
57219ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
57319ea8026Sopenharmony_cistatic int lfs1_traverse(lfs_t *lfs,
57419ea8026Sopenharmony_ci        int (*cb)(void*, lfs_block_t), void *data);
57519ea8026Sopenharmony_ci#endif
57619ea8026Sopenharmony_ci
57719ea8026Sopenharmony_cistatic int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir);
57819ea8026Sopenharmony_ci
57919ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file,
58019ea8026Sopenharmony_ci        void *buffer, lfs_size_t size);
58119ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file,
58219ea8026Sopenharmony_ci        void *buffer, lfs_size_t size);
58319ea8026Sopenharmony_cistatic int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file);
58419ea8026Sopenharmony_cistatic lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file);
58519ea8026Sopenharmony_ci
58619ea8026Sopenharmony_cistatic lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs);
58719ea8026Sopenharmony_cistatic int lfs_fs_rawtraverse(lfs_t *lfs,
58819ea8026Sopenharmony_ci        int (*cb)(void *data, lfs_block_t block), void *data,
58919ea8026Sopenharmony_ci        bool includeorphans);
59019ea8026Sopenharmony_ci
59119ea8026Sopenharmony_cistatic int lfs_deinit(lfs_t *lfs);
59219ea8026Sopenharmony_cistatic int lfs_rawunmount(lfs_t *lfs);
59319ea8026Sopenharmony_ci
59419ea8026Sopenharmony_ci
59519ea8026Sopenharmony_ci/// Block allocator ///
59619ea8026Sopenharmony_ci#ifndef LFS_READONLY
59719ea8026Sopenharmony_cistatic int lfs_alloc_lookahead(void *p, lfs_block_t block) {
59819ea8026Sopenharmony_ci    lfs_t *lfs = (lfs_t*)p;
59919ea8026Sopenharmony_ci    lfs_block_t off = ((block - lfs->free.off)
60019ea8026Sopenharmony_ci            + lfs->block_count) % lfs->block_count;
60119ea8026Sopenharmony_ci
60219ea8026Sopenharmony_ci    if (off < lfs->free.size) {
60319ea8026Sopenharmony_ci        lfs->free.buffer[off / 32] |= 1U << (off % 32);
60419ea8026Sopenharmony_ci    }
60519ea8026Sopenharmony_ci
60619ea8026Sopenharmony_ci    return 0;
60719ea8026Sopenharmony_ci}
60819ea8026Sopenharmony_ci#endif
60919ea8026Sopenharmony_ci
61019ea8026Sopenharmony_ci// indicate allocated blocks have been committed into the filesystem, this
61119ea8026Sopenharmony_ci// is to prevent blocks from being garbage collected in the middle of a
61219ea8026Sopenharmony_ci// commit operation
61319ea8026Sopenharmony_cistatic void lfs_alloc_ack(lfs_t *lfs) {
61419ea8026Sopenharmony_ci    lfs->free.ack = lfs->block_count;
61519ea8026Sopenharmony_ci}
61619ea8026Sopenharmony_ci
61719ea8026Sopenharmony_ci// drop the lookahead buffer, this is done during mounting and failed
61819ea8026Sopenharmony_ci// traversals in order to avoid invalid lookahead state
61919ea8026Sopenharmony_cistatic void lfs_alloc_drop(lfs_t *lfs) {
62019ea8026Sopenharmony_ci    lfs->free.size = 0;
62119ea8026Sopenharmony_ci    lfs->free.i = 0;
62219ea8026Sopenharmony_ci    lfs_alloc_ack(lfs);
62319ea8026Sopenharmony_ci}
62419ea8026Sopenharmony_ci
62519ea8026Sopenharmony_ci#ifndef LFS_READONLY
62619ea8026Sopenharmony_cistatic int lfs_fs_rawgc(lfs_t *lfs) {
62719ea8026Sopenharmony_ci    // Move free offset at the first unused block (lfs->free.i)
62819ea8026Sopenharmony_ci    // lfs->free.i is equal lfs->free.size when all blocks are used
62919ea8026Sopenharmony_ci    lfs->free.off = (lfs->free.off + lfs->free.i) % lfs->block_count;
63019ea8026Sopenharmony_ci    lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size, lfs->free.ack);
63119ea8026Sopenharmony_ci    lfs->free.i = 0;
63219ea8026Sopenharmony_ci
63319ea8026Sopenharmony_ci    // find mask of free blocks from tree
63419ea8026Sopenharmony_ci    memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
63519ea8026Sopenharmony_ci    int err = lfs_fs_rawtraverse(lfs, lfs_alloc_lookahead, lfs, true);
63619ea8026Sopenharmony_ci    if (err) {
63719ea8026Sopenharmony_ci        lfs_alloc_drop(lfs);
63819ea8026Sopenharmony_ci        return err;
63919ea8026Sopenharmony_ci    }
64019ea8026Sopenharmony_ci
64119ea8026Sopenharmony_ci    return 0;
64219ea8026Sopenharmony_ci}
64319ea8026Sopenharmony_ci#endif
64419ea8026Sopenharmony_ci
64519ea8026Sopenharmony_ci#ifndef LFS_READONLY
64619ea8026Sopenharmony_cistatic int lfs_alloc(lfs_t *lfs, lfs_block_t *block) {
64719ea8026Sopenharmony_ci    while (true) {
64819ea8026Sopenharmony_ci        while (lfs->free.i != lfs->free.size) {
64919ea8026Sopenharmony_ci            lfs_block_t off = lfs->free.i;
65019ea8026Sopenharmony_ci            lfs->free.i += 1;
65119ea8026Sopenharmony_ci            lfs->free.ack -= 1;
65219ea8026Sopenharmony_ci
65319ea8026Sopenharmony_ci            if (!(lfs->free.buffer[off / 32] & (1U << (off % 32)))) {
65419ea8026Sopenharmony_ci                // found a free block
65519ea8026Sopenharmony_ci                *block = (lfs->free.off + off) % lfs->block_count;
65619ea8026Sopenharmony_ci
65719ea8026Sopenharmony_ci                // eagerly find next off so an alloc ack can
65819ea8026Sopenharmony_ci                // discredit old lookahead blocks
65919ea8026Sopenharmony_ci                while (lfs->free.i != lfs->free.size &&
66019ea8026Sopenharmony_ci                        (lfs->free.buffer[lfs->free.i / 32]
66119ea8026Sopenharmony_ci                            & (1U << (lfs->free.i % 32)))) {
66219ea8026Sopenharmony_ci                    lfs->free.i += 1;
66319ea8026Sopenharmony_ci                    lfs->free.ack -= 1;
66419ea8026Sopenharmony_ci                }
66519ea8026Sopenharmony_ci
66619ea8026Sopenharmony_ci                return 0;
66719ea8026Sopenharmony_ci            }
66819ea8026Sopenharmony_ci        }
66919ea8026Sopenharmony_ci
67019ea8026Sopenharmony_ci        // check if we have looked at all blocks since last ack
67119ea8026Sopenharmony_ci        if (lfs->free.ack == 0) {
67219ea8026Sopenharmony_ci            LFS_ERROR("No more free space %"PRIu32,
67319ea8026Sopenharmony_ci                    lfs->free.i + lfs->free.off);
67419ea8026Sopenharmony_ci            return LFS_ERR_NOSPC;
67519ea8026Sopenharmony_ci        }
67619ea8026Sopenharmony_ci
67719ea8026Sopenharmony_ci        int err = lfs_fs_rawgc(lfs);
67819ea8026Sopenharmony_ci        if(err) {
67919ea8026Sopenharmony_ci            return err;
68019ea8026Sopenharmony_ci        }
68119ea8026Sopenharmony_ci    }
68219ea8026Sopenharmony_ci}
68319ea8026Sopenharmony_ci#endif
68419ea8026Sopenharmony_ci
68519ea8026Sopenharmony_ci/// Metadata pair and directory operations ///
68619ea8026Sopenharmony_cistatic lfs_stag_t lfs_dir_getslice(lfs_t *lfs, const lfs_mdir_t *dir,
68719ea8026Sopenharmony_ci        lfs_tag_t gmask, lfs_tag_t gtag,
68819ea8026Sopenharmony_ci        lfs_off_t goff, void *gbuffer, lfs_size_t gsize) {
68919ea8026Sopenharmony_ci    lfs_off_t off = dir->off;
69019ea8026Sopenharmony_ci    lfs_tag_t ntag = dir->etag;
69119ea8026Sopenharmony_ci    lfs_stag_t gdiff = 0;
69219ea8026Sopenharmony_ci
69319ea8026Sopenharmony_ci    if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair) &&
69419ea8026Sopenharmony_ci            lfs_tag_id(gmask) != 0 &&
69519ea8026Sopenharmony_ci            lfs_tag_id(lfs->gdisk.tag) <= lfs_tag_id(gtag)) {
69619ea8026Sopenharmony_ci        // synthetic moves
69719ea8026Sopenharmony_ci        gdiff -= LFS_MKTAG(0, 1, 0);
69819ea8026Sopenharmony_ci    }
69919ea8026Sopenharmony_ci
70019ea8026Sopenharmony_ci    // iterate over dir block backwards (for faster lookups)
70119ea8026Sopenharmony_ci    while (off >= sizeof(lfs_tag_t) + lfs_tag_dsize(ntag)) {
70219ea8026Sopenharmony_ci        off -= lfs_tag_dsize(ntag);
70319ea8026Sopenharmony_ci        lfs_tag_t tag = ntag;
70419ea8026Sopenharmony_ci        int err = lfs_bd_read(lfs,
70519ea8026Sopenharmony_ci                NULL, &lfs->rcache, sizeof(ntag),
70619ea8026Sopenharmony_ci                dir->pair[0], off, &ntag, sizeof(ntag));
70719ea8026Sopenharmony_ci        if (err) {
70819ea8026Sopenharmony_ci            return err;
70919ea8026Sopenharmony_ci        }
71019ea8026Sopenharmony_ci
71119ea8026Sopenharmony_ci        ntag = (lfs_frombe32(ntag) ^ tag) & 0x7fffffff;
71219ea8026Sopenharmony_ci
71319ea8026Sopenharmony_ci        if (lfs_tag_id(gmask) != 0 &&
71419ea8026Sopenharmony_ci                lfs_tag_type1(tag) == LFS_TYPE_SPLICE &&
71519ea8026Sopenharmony_ci                lfs_tag_id(tag) <= lfs_tag_id(gtag - gdiff)) {
71619ea8026Sopenharmony_ci            if (tag == (LFS_MKTAG(LFS_TYPE_CREATE, 0, 0) |
71719ea8026Sopenharmony_ci                    (LFS_MKTAG(0, 0x3ff, 0) & (gtag - gdiff)))) {
71819ea8026Sopenharmony_ci                // found where we were created
71919ea8026Sopenharmony_ci                return LFS_ERR_NOENT;
72019ea8026Sopenharmony_ci            }
72119ea8026Sopenharmony_ci
72219ea8026Sopenharmony_ci            // move around splices
72319ea8026Sopenharmony_ci            gdiff += LFS_MKTAG(0, lfs_tag_splice(tag), 0);
72419ea8026Sopenharmony_ci        }
72519ea8026Sopenharmony_ci
72619ea8026Sopenharmony_ci        if ((gmask & tag) == (gmask & (gtag - gdiff))) {
72719ea8026Sopenharmony_ci            if (lfs_tag_isdelete(tag)) {
72819ea8026Sopenharmony_ci                return LFS_ERR_NOENT;
72919ea8026Sopenharmony_ci            }
73019ea8026Sopenharmony_ci
73119ea8026Sopenharmony_ci            lfs_size_t diff = lfs_min(lfs_tag_size(tag), gsize);
73219ea8026Sopenharmony_ci            err = lfs_bd_read(lfs,
73319ea8026Sopenharmony_ci                    NULL, &lfs->rcache, diff,
73419ea8026Sopenharmony_ci                    dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff);
73519ea8026Sopenharmony_ci            if (err) {
73619ea8026Sopenharmony_ci                return err;
73719ea8026Sopenharmony_ci            }
73819ea8026Sopenharmony_ci
73919ea8026Sopenharmony_ci            memset((uint8_t*)gbuffer + diff, 0, gsize - diff);
74019ea8026Sopenharmony_ci
74119ea8026Sopenharmony_ci            return tag + gdiff;
74219ea8026Sopenharmony_ci        }
74319ea8026Sopenharmony_ci    }
74419ea8026Sopenharmony_ci
74519ea8026Sopenharmony_ci    return LFS_ERR_NOENT;
74619ea8026Sopenharmony_ci}
74719ea8026Sopenharmony_ci
74819ea8026Sopenharmony_cistatic lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir,
74919ea8026Sopenharmony_ci        lfs_tag_t gmask, lfs_tag_t gtag, void *buffer) {
75019ea8026Sopenharmony_ci    return lfs_dir_getslice(lfs, dir,
75119ea8026Sopenharmony_ci            gmask, gtag,
75219ea8026Sopenharmony_ci            0, buffer, lfs_tag_size(gtag));
75319ea8026Sopenharmony_ci}
75419ea8026Sopenharmony_ci
75519ea8026Sopenharmony_cistatic int lfs_dir_getread(lfs_t *lfs, const lfs_mdir_t *dir,
75619ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache, lfs_size_t hint,
75719ea8026Sopenharmony_ci        lfs_tag_t gmask, lfs_tag_t gtag,
75819ea8026Sopenharmony_ci        lfs_off_t off, void *buffer, lfs_size_t size) {
75919ea8026Sopenharmony_ci    uint8_t *data = buffer;
76019ea8026Sopenharmony_ci    if (off+size > lfs->cfg->block_size) {
76119ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
76219ea8026Sopenharmony_ci    }
76319ea8026Sopenharmony_ci
76419ea8026Sopenharmony_ci    while (size > 0) {
76519ea8026Sopenharmony_ci        lfs_size_t diff = size;
76619ea8026Sopenharmony_ci
76719ea8026Sopenharmony_ci        if (pcache && pcache->block == LFS_BLOCK_INLINE &&
76819ea8026Sopenharmony_ci                off < pcache->off + pcache->size) {
76919ea8026Sopenharmony_ci            if (off >= pcache->off) {
77019ea8026Sopenharmony_ci                // is already in pcache?
77119ea8026Sopenharmony_ci                diff = lfs_min(diff, pcache->size - (off-pcache->off));
77219ea8026Sopenharmony_ci                memcpy(data, &pcache->buffer[off-pcache->off], diff);
77319ea8026Sopenharmony_ci
77419ea8026Sopenharmony_ci                data += diff;
77519ea8026Sopenharmony_ci                off += diff;
77619ea8026Sopenharmony_ci                size -= diff;
77719ea8026Sopenharmony_ci                continue;
77819ea8026Sopenharmony_ci            }
77919ea8026Sopenharmony_ci
78019ea8026Sopenharmony_ci            // pcache takes priority
78119ea8026Sopenharmony_ci            diff = lfs_min(diff, pcache->off-off);
78219ea8026Sopenharmony_ci        }
78319ea8026Sopenharmony_ci
78419ea8026Sopenharmony_ci        if (rcache->block == LFS_BLOCK_INLINE &&
78519ea8026Sopenharmony_ci                off < rcache->off + rcache->size) {
78619ea8026Sopenharmony_ci            if (off >= rcache->off) {
78719ea8026Sopenharmony_ci                // is already in rcache?
78819ea8026Sopenharmony_ci                diff = lfs_min(diff, rcache->size - (off-rcache->off));
78919ea8026Sopenharmony_ci                memcpy(data, &rcache->buffer[off-rcache->off], diff);
79019ea8026Sopenharmony_ci
79119ea8026Sopenharmony_ci                data += diff;
79219ea8026Sopenharmony_ci                off += diff;
79319ea8026Sopenharmony_ci                size -= diff;
79419ea8026Sopenharmony_ci                continue;
79519ea8026Sopenharmony_ci            }
79619ea8026Sopenharmony_ci
79719ea8026Sopenharmony_ci            // rcache takes priority
79819ea8026Sopenharmony_ci            diff = lfs_min(diff, rcache->off-off);
79919ea8026Sopenharmony_ci        }
80019ea8026Sopenharmony_ci
80119ea8026Sopenharmony_ci        // load to cache, first condition can no longer fail
80219ea8026Sopenharmony_ci        rcache->block = LFS_BLOCK_INLINE;
80319ea8026Sopenharmony_ci        rcache->off = lfs_aligndown(off, lfs->cfg->read_size);
80419ea8026Sopenharmony_ci        rcache->size = lfs_min(lfs_alignup(off+hint, lfs->cfg->read_size),
80519ea8026Sopenharmony_ci                lfs->cfg->cache_size);
80619ea8026Sopenharmony_ci        int err = lfs_dir_getslice(lfs, dir, gmask, gtag,
80719ea8026Sopenharmony_ci                rcache->off, rcache->buffer, rcache->size);
80819ea8026Sopenharmony_ci        if (err < 0) {
80919ea8026Sopenharmony_ci            return err;
81019ea8026Sopenharmony_ci        }
81119ea8026Sopenharmony_ci    }
81219ea8026Sopenharmony_ci
81319ea8026Sopenharmony_ci    return 0;
81419ea8026Sopenharmony_ci}
81519ea8026Sopenharmony_ci
81619ea8026Sopenharmony_ci#ifndef LFS_READONLY
81719ea8026Sopenharmony_cistatic int lfs_dir_traverse_filter(void *p,
81819ea8026Sopenharmony_ci        lfs_tag_t tag, const void *buffer) {
81919ea8026Sopenharmony_ci    lfs_tag_t *filtertag = p;
82019ea8026Sopenharmony_ci    (void)buffer;
82119ea8026Sopenharmony_ci
82219ea8026Sopenharmony_ci    // which mask depends on unique bit in tag structure
82319ea8026Sopenharmony_ci    uint32_t mask = (tag & LFS_MKTAG(0x100, 0, 0))
82419ea8026Sopenharmony_ci            ? LFS_MKTAG(0x7ff, 0x3ff, 0)
82519ea8026Sopenharmony_ci            : LFS_MKTAG(0x700, 0x3ff, 0);
82619ea8026Sopenharmony_ci
82719ea8026Sopenharmony_ci    // check for redundancy
82819ea8026Sopenharmony_ci    if ((mask & tag) == (mask & *filtertag) ||
82919ea8026Sopenharmony_ci            lfs_tag_isdelete(*filtertag) ||
83019ea8026Sopenharmony_ci            (LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) == (
83119ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) |
83219ea8026Sopenharmony_ci                    (LFS_MKTAG(0, 0x3ff, 0) & *filtertag))) {
83319ea8026Sopenharmony_ci        *filtertag = LFS_MKTAG(LFS_FROM_NOOP, 0, 0);
83419ea8026Sopenharmony_ci        return true;
83519ea8026Sopenharmony_ci    }
83619ea8026Sopenharmony_ci
83719ea8026Sopenharmony_ci    // check if we need to adjust for created/deleted tags
83819ea8026Sopenharmony_ci    if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE &&
83919ea8026Sopenharmony_ci            lfs_tag_id(tag) <= lfs_tag_id(*filtertag)) {
84019ea8026Sopenharmony_ci        *filtertag += LFS_MKTAG(0, lfs_tag_splice(tag), 0);
84119ea8026Sopenharmony_ci    }
84219ea8026Sopenharmony_ci
84319ea8026Sopenharmony_ci    return false;
84419ea8026Sopenharmony_ci}
84519ea8026Sopenharmony_ci#endif
84619ea8026Sopenharmony_ci
84719ea8026Sopenharmony_ci#ifndef LFS_READONLY
84819ea8026Sopenharmony_ci// maximum recursive depth of lfs_dir_traverse, the deepest call:
84919ea8026Sopenharmony_ci//
85019ea8026Sopenharmony_ci// traverse with commit
85119ea8026Sopenharmony_ci// '-> traverse with move
85219ea8026Sopenharmony_ci//     '-> traverse with filter
85319ea8026Sopenharmony_ci//
85419ea8026Sopenharmony_ci#define LFS_DIR_TRAVERSE_DEPTH 3
85519ea8026Sopenharmony_ci
85619ea8026Sopenharmony_cistruct lfs_dir_traverse {
85719ea8026Sopenharmony_ci    const lfs_mdir_t *dir;
85819ea8026Sopenharmony_ci    lfs_off_t off;
85919ea8026Sopenharmony_ci    lfs_tag_t ptag;
86019ea8026Sopenharmony_ci    const struct lfs_mattr *attrs;
86119ea8026Sopenharmony_ci    int attrcount;
86219ea8026Sopenharmony_ci
86319ea8026Sopenharmony_ci    lfs_tag_t tmask;
86419ea8026Sopenharmony_ci    lfs_tag_t ttag;
86519ea8026Sopenharmony_ci    uint16_t begin;
86619ea8026Sopenharmony_ci    uint16_t end;
86719ea8026Sopenharmony_ci    int16_t diff;
86819ea8026Sopenharmony_ci
86919ea8026Sopenharmony_ci    int (*cb)(void *data, lfs_tag_t tag, const void *buffer);
87019ea8026Sopenharmony_ci    void *data;
87119ea8026Sopenharmony_ci
87219ea8026Sopenharmony_ci    lfs_tag_t tag;
87319ea8026Sopenharmony_ci    const void *buffer;
87419ea8026Sopenharmony_ci    struct lfs_diskoff disk;
87519ea8026Sopenharmony_ci};
87619ea8026Sopenharmony_ci
87719ea8026Sopenharmony_cistatic int lfs_dir_traverse(lfs_t *lfs,
87819ea8026Sopenharmony_ci        const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag,
87919ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount,
88019ea8026Sopenharmony_ci        lfs_tag_t tmask, lfs_tag_t ttag,
88119ea8026Sopenharmony_ci        uint16_t begin, uint16_t end, int16_t diff,
88219ea8026Sopenharmony_ci        int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) {
88319ea8026Sopenharmony_ci    // This function in inherently recursive, but bounded. To allow tool-based
88419ea8026Sopenharmony_ci    // analysis without unnecessary code-cost we use an explicit stack
88519ea8026Sopenharmony_ci    struct lfs_dir_traverse stack[LFS_DIR_TRAVERSE_DEPTH-1];
88619ea8026Sopenharmony_ci    unsigned sp = 0;
88719ea8026Sopenharmony_ci    int res;
88819ea8026Sopenharmony_ci
88919ea8026Sopenharmony_ci    // iterate over directory and attrs
89019ea8026Sopenharmony_ci    lfs_tag_t tag;
89119ea8026Sopenharmony_ci    const void *buffer;
89219ea8026Sopenharmony_ci    struct lfs_diskoff disk = {0};
89319ea8026Sopenharmony_ci    while (true) {
89419ea8026Sopenharmony_ci        {
89519ea8026Sopenharmony_ci            if (off+lfs_tag_dsize(ptag) < dir->off) {
89619ea8026Sopenharmony_ci                off += lfs_tag_dsize(ptag);
89719ea8026Sopenharmony_ci                int err = lfs_bd_read(lfs,
89819ea8026Sopenharmony_ci                        NULL, &lfs->rcache, sizeof(tag),
89919ea8026Sopenharmony_ci                        dir->pair[0], off, &tag, sizeof(tag));
90019ea8026Sopenharmony_ci                if (err) {
90119ea8026Sopenharmony_ci                    return err;
90219ea8026Sopenharmony_ci                }
90319ea8026Sopenharmony_ci
90419ea8026Sopenharmony_ci                tag = (lfs_frombe32(tag) ^ ptag) | 0x80000000;
90519ea8026Sopenharmony_ci                disk.block = dir->pair[0];
90619ea8026Sopenharmony_ci                disk.off = off+sizeof(lfs_tag_t);
90719ea8026Sopenharmony_ci                buffer = &disk;
90819ea8026Sopenharmony_ci                ptag = tag;
90919ea8026Sopenharmony_ci            } else if (attrcount > 0) {
91019ea8026Sopenharmony_ci                tag = attrs[0].tag;
91119ea8026Sopenharmony_ci                buffer = attrs[0].buffer;
91219ea8026Sopenharmony_ci                attrs += 1;
91319ea8026Sopenharmony_ci                attrcount -= 1;
91419ea8026Sopenharmony_ci            } else {
91519ea8026Sopenharmony_ci                // finished traversal, pop from stack?
91619ea8026Sopenharmony_ci                res = 0;
91719ea8026Sopenharmony_ci                break;
91819ea8026Sopenharmony_ci            }
91919ea8026Sopenharmony_ci
92019ea8026Sopenharmony_ci            // do we need to filter?
92119ea8026Sopenharmony_ci            lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0);
92219ea8026Sopenharmony_ci            if ((mask & tmask & tag) != (mask & tmask & ttag)) {
92319ea8026Sopenharmony_ci                continue;
92419ea8026Sopenharmony_ci            }
92519ea8026Sopenharmony_ci
92619ea8026Sopenharmony_ci            if (lfs_tag_id(tmask) != 0) {
92719ea8026Sopenharmony_ci                LFS_ASSERT(sp < LFS_DIR_TRAVERSE_DEPTH);
92819ea8026Sopenharmony_ci                // recurse, scan for duplicates, and update tag based on
92919ea8026Sopenharmony_ci                // creates/deletes
93019ea8026Sopenharmony_ci                stack[sp] = (struct lfs_dir_traverse){
93119ea8026Sopenharmony_ci                    .dir        = dir,
93219ea8026Sopenharmony_ci                    .off        = off,
93319ea8026Sopenharmony_ci                    .ptag       = ptag,
93419ea8026Sopenharmony_ci                    .attrs      = attrs,
93519ea8026Sopenharmony_ci                    .attrcount  = attrcount,
93619ea8026Sopenharmony_ci                    .tmask      = tmask,
93719ea8026Sopenharmony_ci                    .ttag       = ttag,
93819ea8026Sopenharmony_ci                    .begin      = begin,
93919ea8026Sopenharmony_ci                    .end        = end,
94019ea8026Sopenharmony_ci                    .diff       = diff,
94119ea8026Sopenharmony_ci                    .cb         = cb,
94219ea8026Sopenharmony_ci                    .data       = data,
94319ea8026Sopenharmony_ci                    .tag        = tag,
94419ea8026Sopenharmony_ci                    .buffer     = buffer,
94519ea8026Sopenharmony_ci                    .disk       = disk,
94619ea8026Sopenharmony_ci                };
94719ea8026Sopenharmony_ci                sp += 1;
94819ea8026Sopenharmony_ci
94919ea8026Sopenharmony_ci                tmask = 0;
95019ea8026Sopenharmony_ci                ttag = 0;
95119ea8026Sopenharmony_ci                begin = 0;
95219ea8026Sopenharmony_ci                end = 0;
95319ea8026Sopenharmony_ci                diff = 0;
95419ea8026Sopenharmony_ci                cb = lfs_dir_traverse_filter;
95519ea8026Sopenharmony_ci                data = &stack[sp-1].tag;
95619ea8026Sopenharmony_ci                continue;
95719ea8026Sopenharmony_ci            }
95819ea8026Sopenharmony_ci        }
95919ea8026Sopenharmony_ci
96019ea8026Sopenharmony_cipopped:
96119ea8026Sopenharmony_ci        // in filter range?
96219ea8026Sopenharmony_ci        if (lfs_tag_id(tmask) != 0 &&
96319ea8026Sopenharmony_ci                !(lfs_tag_id(tag) >= begin && lfs_tag_id(tag) < end)) {
96419ea8026Sopenharmony_ci            continue;
96519ea8026Sopenharmony_ci        }
96619ea8026Sopenharmony_ci
96719ea8026Sopenharmony_ci        // handle special cases for mcu-side operations
96819ea8026Sopenharmony_ci        if (lfs_tag_type3(tag) == LFS_FROM_NOOP) {
96919ea8026Sopenharmony_ci            // do nothing
97019ea8026Sopenharmony_ci        } else if (lfs_tag_type3(tag) == LFS_FROM_MOVE) {
97119ea8026Sopenharmony_ci            // Without this condition, lfs_dir_traverse can exhibit an
97219ea8026Sopenharmony_ci            // extremely expensive O(n^3) of nested loops when renaming.
97319ea8026Sopenharmony_ci            // This happens because lfs_dir_traverse tries to filter tags by
97419ea8026Sopenharmony_ci            // the tags in the source directory, triggering a second
97519ea8026Sopenharmony_ci            // lfs_dir_traverse with its own filter operation.
97619ea8026Sopenharmony_ci            //
97719ea8026Sopenharmony_ci            // traverse with commit
97819ea8026Sopenharmony_ci            // '-> traverse with filter
97919ea8026Sopenharmony_ci            //     '-> traverse with move
98019ea8026Sopenharmony_ci            //         '-> traverse with filter
98119ea8026Sopenharmony_ci            //
98219ea8026Sopenharmony_ci            // However we don't actually care about filtering the second set of
98319ea8026Sopenharmony_ci            // tags, since duplicate tags have no effect when filtering.
98419ea8026Sopenharmony_ci            //
98519ea8026Sopenharmony_ci            // This check skips this unnecessary recursive filtering explicitly,
98619ea8026Sopenharmony_ci            // reducing this runtime from O(n^3) to O(n^2).
98719ea8026Sopenharmony_ci            if (cb == lfs_dir_traverse_filter) {
98819ea8026Sopenharmony_ci                continue;
98919ea8026Sopenharmony_ci            }
99019ea8026Sopenharmony_ci
99119ea8026Sopenharmony_ci            // recurse into move
99219ea8026Sopenharmony_ci            stack[sp] = (struct lfs_dir_traverse){
99319ea8026Sopenharmony_ci                .dir        = dir,
99419ea8026Sopenharmony_ci                .off        = off,
99519ea8026Sopenharmony_ci                .ptag       = ptag,
99619ea8026Sopenharmony_ci                .attrs      = attrs,
99719ea8026Sopenharmony_ci                .attrcount  = attrcount,
99819ea8026Sopenharmony_ci                .tmask      = tmask,
99919ea8026Sopenharmony_ci                .ttag       = ttag,
100019ea8026Sopenharmony_ci                .begin      = begin,
100119ea8026Sopenharmony_ci                .end        = end,
100219ea8026Sopenharmony_ci                .diff       = diff,
100319ea8026Sopenharmony_ci                .cb         = cb,
100419ea8026Sopenharmony_ci                .data       = data,
100519ea8026Sopenharmony_ci                .tag        = LFS_MKTAG(LFS_FROM_NOOP, 0, 0),
100619ea8026Sopenharmony_ci            };
100719ea8026Sopenharmony_ci            sp += 1;
100819ea8026Sopenharmony_ci
100919ea8026Sopenharmony_ci            uint16_t fromid = lfs_tag_size(tag);
101019ea8026Sopenharmony_ci            uint16_t toid = lfs_tag_id(tag);
101119ea8026Sopenharmony_ci            dir = buffer;
101219ea8026Sopenharmony_ci            off = 0;
101319ea8026Sopenharmony_ci            ptag = 0xffffffff;
101419ea8026Sopenharmony_ci            attrs = NULL;
101519ea8026Sopenharmony_ci            attrcount = 0;
101619ea8026Sopenharmony_ci            tmask = LFS_MKTAG(0x600, 0x3ff, 0);
101719ea8026Sopenharmony_ci            ttag = LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0);
101819ea8026Sopenharmony_ci            begin = fromid;
101919ea8026Sopenharmony_ci            end = fromid+1;
102019ea8026Sopenharmony_ci            diff = toid-fromid+diff;
102119ea8026Sopenharmony_ci        } else if (lfs_tag_type3(tag) == LFS_FROM_USERATTRS) {
102219ea8026Sopenharmony_ci            for (unsigned i = 0; i < lfs_tag_size(tag); i++) {
102319ea8026Sopenharmony_ci                const struct lfs_attr *a = buffer;
102419ea8026Sopenharmony_ci                res = cb(data, LFS_MKTAG(LFS_TYPE_USERATTR + a[i].type,
102519ea8026Sopenharmony_ci                        lfs_tag_id(tag) + diff, a[i].size), a[i].buffer);
102619ea8026Sopenharmony_ci                if (res < 0) {
102719ea8026Sopenharmony_ci                    return res;
102819ea8026Sopenharmony_ci                }
102919ea8026Sopenharmony_ci
103019ea8026Sopenharmony_ci                if (res) {
103119ea8026Sopenharmony_ci                    break;
103219ea8026Sopenharmony_ci                }
103319ea8026Sopenharmony_ci            }
103419ea8026Sopenharmony_ci        } else {
103519ea8026Sopenharmony_ci            res = cb(data, tag + LFS_MKTAG(0, diff, 0), buffer);
103619ea8026Sopenharmony_ci            if (res < 0) {
103719ea8026Sopenharmony_ci                return res;
103819ea8026Sopenharmony_ci            }
103919ea8026Sopenharmony_ci
104019ea8026Sopenharmony_ci            if (res) {
104119ea8026Sopenharmony_ci                break;
104219ea8026Sopenharmony_ci            }
104319ea8026Sopenharmony_ci        }
104419ea8026Sopenharmony_ci    }
104519ea8026Sopenharmony_ci
104619ea8026Sopenharmony_ci    if (sp > 0) {
104719ea8026Sopenharmony_ci        // pop from the stack and return, fortunately all pops share
104819ea8026Sopenharmony_ci        // a destination
104919ea8026Sopenharmony_ci        dir         = stack[sp-1].dir;
105019ea8026Sopenharmony_ci        off         = stack[sp-1].off;
105119ea8026Sopenharmony_ci        ptag        = stack[sp-1].ptag;
105219ea8026Sopenharmony_ci        attrs       = stack[sp-1].attrs;
105319ea8026Sopenharmony_ci        attrcount   = stack[sp-1].attrcount;
105419ea8026Sopenharmony_ci        tmask       = stack[sp-1].tmask;
105519ea8026Sopenharmony_ci        ttag        = stack[sp-1].ttag;
105619ea8026Sopenharmony_ci        begin       = stack[sp-1].begin;
105719ea8026Sopenharmony_ci        end         = stack[sp-1].end;
105819ea8026Sopenharmony_ci        diff        = stack[sp-1].diff;
105919ea8026Sopenharmony_ci        cb          = stack[sp-1].cb;
106019ea8026Sopenharmony_ci        data        = stack[sp-1].data;
106119ea8026Sopenharmony_ci        tag         = stack[sp-1].tag;
106219ea8026Sopenharmony_ci        buffer      = stack[sp-1].buffer;
106319ea8026Sopenharmony_ci        disk        = stack[sp-1].disk;
106419ea8026Sopenharmony_ci        sp -= 1;
106519ea8026Sopenharmony_ci        goto popped;
106619ea8026Sopenharmony_ci    } else {
106719ea8026Sopenharmony_ci        return res;
106819ea8026Sopenharmony_ci    }
106919ea8026Sopenharmony_ci}
107019ea8026Sopenharmony_ci#endif
107119ea8026Sopenharmony_ci
107219ea8026Sopenharmony_cistatic lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs,
107319ea8026Sopenharmony_ci        lfs_mdir_t *dir, const lfs_block_t pair[2],
107419ea8026Sopenharmony_ci        lfs_tag_t fmask, lfs_tag_t ftag, uint16_t *id,
107519ea8026Sopenharmony_ci        int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) {
107619ea8026Sopenharmony_ci    // we can find tag very efficiently during a fetch, since we're already
107719ea8026Sopenharmony_ci    // scanning the entire directory
107819ea8026Sopenharmony_ci    lfs_stag_t besttag = -1;
107919ea8026Sopenharmony_ci
108019ea8026Sopenharmony_ci    // if either block address is invalid we return LFS_ERR_CORRUPT here,
108119ea8026Sopenharmony_ci    // otherwise later writes to the pair could fail
108219ea8026Sopenharmony_ci    if (lfs->block_count
108319ea8026Sopenharmony_ci            && (pair[0] >= lfs->block_count || pair[1] >= lfs->block_count)) {
108419ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
108519ea8026Sopenharmony_ci    }
108619ea8026Sopenharmony_ci
108719ea8026Sopenharmony_ci    // find the block with the most recent revision
108819ea8026Sopenharmony_ci    uint32_t revs[2] = {0, 0};
108919ea8026Sopenharmony_ci    int r = 0;
109019ea8026Sopenharmony_ci    for (int i = 0; i < 2; i++) {
109119ea8026Sopenharmony_ci        int err = lfs_bd_read(lfs,
109219ea8026Sopenharmony_ci                NULL, &lfs->rcache, sizeof(revs[i]),
109319ea8026Sopenharmony_ci                pair[i], 0, &revs[i], sizeof(revs[i]));
109419ea8026Sopenharmony_ci        revs[i] = lfs_fromle32(revs[i]);
109519ea8026Sopenharmony_ci        if (err && err != LFS_ERR_CORRUPT) {
109619ea8026Sopenharmony_ci            return err;
109719ea8026Sopenharmony_ci        }
109819ea8026Sopenharmony_ci
109919ea8026Sopenharmony_ci        if (err != LFS_ERR_CORRUPT &&
110019ea8026Sopenharmony_ci                lfs_scmp(revs[i], revs[(i+1)%2]) > 0) {
110119ea8026Sopenharmony_ci            r = i;
110219ea8026Sopenharmony_ci        }
110319ea8026Sopenharmony_ci    }
110419ea8026Sopenharmony_ci
110519ea8026Sopenharmony_ci    dir->pair[0] = pair[(r+0)%2];
110619ea8026Sopenharmony_ci    dir->pair[1] = pair[(r+1)%2];
110719ea8026Sopenharmony_ci    dir->rev = revs[(r+0)%2];
110819ea8026Sopenharmony_ci    dir->off = 0; // nonzero = found some commits
110919ea8026Sopenharmony_ci
111019ea8026Sopenharmony_ci    // now scan tags to fetch the actual dir and find possible match
111119ea8026Sopenharmony_ci    for (int i = 0; i < 2; i++) {
111219ea8026Sopenharmony_ci        lfs_off_t off = 0;
111319ea8026Sopenharmony_ci        lfs_tag_t ptag = 0xffffffff;
111419ea8026Sopenharmony_ci
111519ea8026Sopenharmony_ci        uint16_t tempcount = 0;
111619ea8026Sopenharmony_ci        lfs_block_t temptail[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
111719ea8026Sopenharmony_ci        bool tempsplit = false;
111819ea8026Sopenharmony_ci        lfs_stag_t tempbesttag = besttag;
111919ea8026Sopenharmony_ci
112019ea8026Sopenharmony_ci        // assume not erased until proven otherwise
112119ea8026Sopenharmony_ci        bool maybeerased = false;
112219ea8026Sopenharmony_ci        bool hasfcrc = false;
112319ea8026Sopenharmony_ci        struct lfs_fcrc fcrc;
112419ea8026Sopenharmony_ci
112519ea8026Sopenharmony_ci        dir->rev = lfs_tole32(dir->rev);
112619ea8026Sopenharmony_ci        uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev));
112719ea8026Sopenharmony_ci        dir->rev = lfs_fromle32(dir->rev);
112819ea8026Sopenharmony_ci
112919ea8026Sopenharmony_ci        while (true) {
113019ea8026Sopenharmony_ci            // extract next tag
113119ea8026Sopenharmony_ci            lfs_tag_t tag;
113219ea8026Sopenharmony_ci            off += lfs_tag_dsize(ptag);
113319ea8026Sopenharmony_ci            int err = lfs_bd_read(lfs,
113419ea8026Sopenharmony_ci                    NULL, &lfs->rcache, lfs->cfg->block_size,
113519ea8026Sopenharmony_ci                    dir->pair[0], off, &tag, sizeof(tag));
113619ea8026Sopenharmony_ci            if (err) {
113719ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
113819ea8026Sopenharmony_ci                    // can't continue?
113919ea8026Sopenharmony_ci                    break;
114019ea8026Sopenharmony_ci                }
114119ea8026Sopenharmony_ci                return err;
114219ea8026Sopenharmony_ci            }
114319ea8026Sopenharmony_ci
114419ea8026Sopenharmony_ci            crc = lfs_crc(crc, &tag, sizeof(tag));
114519ea8026Sopenharmony_ci            tag = lfs_frombe32(tag) ^ ptag;
114619ea8026Sopenharmony_ci
114719ea8026Sopenharmony_ci            // next commit not yet programmed?
114819ea8026Sopenharmony_ci            if (!lfs_tag_isvalid(tag)) {
114919ea8026Sopenharmony_ci                // we only might be erased if the last tag was a crc
115019ea8026Sopenharmony_ci                maybeerased = (lfs_tag_type2(ptag) == LFS_TYPE_CCRC);
115119ea8026Sopenharmony_ci                break;
115219ea8026Sopenharmony_ci            // out of range?
115319ea8026Sopenharmony_ci            } else if (off + lfs_tag_dsize(tag) > lfs->cfg->block_size) {
115419ea8026Sopenharmony_ci                break;
115519ea8026Sopenharmony_ci            }
115619ea8026Sopenharmony_ci
115719ea8026Sopenharmony_ci            ptag = tag;
115819ea8026Sopenharmony_ci
115919ea8026Sopenharmony_ci            if (lfs_tag_type2(tag) == LFS_TYPE_CCRC) {
116019ea8026Sopenharmony_ci                // check the crc attr
116119ea8026Sopenharmony_ci                uint32_t dcrc;
116219ea8026Sopenharmony_ci                err = lfs_bd_read(lfs,
116319ea8026Sopenharmony_ci                        NULL, &lfs->rcache, lfs->cfg->block_size,
116419ea8026Sopenharmony_ci                        dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc));
116519ea8026Sopenharmony_ci                if (err) {
116619ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
116719ea8026Sopenharmony_ci                        break;
116819ea8026Sopenharmony_ci                    }
116919ea8026Sopenharmony_ci                    return err;
117019ea8026Sopenharmony_ci                }
117119ea8026Sopenharmony_ci                dcrc = lfs_fromle32(dcrc);
117219ea8026Sopenharmony_ci
117319ea8026Sopenharmony_ci                if (crc != dcrc) {
117419ea8026Sopenharmony_ci                    break;
117519ea8026Sopenharmony_ci                }
117619ea8026Sopenharmony_ci
117719ea8026Sopenharmony_ci                // reset the next bit if we need to
117819ea8026Sopenharmony_ci                ptag ^= (lfs_tag_t)(lfs_tag_chunk(tag) & 1U) << 31;
117919ea8026Sopenharmony_ci
118019ea8026Sopenharmony_ci                // toss our crc into the filesystem seed for
118119ea8026Sopenharmony_ci                // pseudorandom numbers, note we use another crc here
118219ea8026Sopenharmony_ci                // as a collection function because it is sufficiently
118319ea8026Sopenharmony_ci                // random and convenient
118419ea8026Sopenharmony_ci                lfs->seed = lfs_crc(lfs->seed, &crc, sizeof(crc));
118519ea8026Sopenharmony_ci
118619ea8026Sopenharmony_ci                // update with what's found so far
118719ea8026Sopenharmony_ci                besttag = tempbesttag;
118819ea8026Sopenharmony_ci                dir->off = off + lfs_tag_dsize(tag);
118919ea8026Sopenharmony_ci                dir->etag = ptag;
119019ea8026Sopenharmony_ci                dir->count = tempcount;
119119ea8026Sopenharmony_ci                dir->tail[0] = temptail[0];
119219ea8026Sopenharmony_ci                dir->tail[1] = temptail[1];
119319ea8026Sopenharmony_ci                dir->split = tempsplit;
119419ea8026Sopenharmony_ci
119519ea8026Sopenharmony_ci                // reset crc, hasfcrc
119619ea8026Sopenharmony_ci                crc = 0xffffffff;
119719ea8026Sopenharmony_ci                continue;
119819ea8026Sopenharmony_ci            }
119919ea8026Sopenharmony_ci
120019ea8026Sopenharmony_ci            // crc the entry first, hopefully leaving it in the cache
120119ea8026Sopenharmony_ci            err = lfs_bd_crc(lfs,
120219ea8026Sopenharmony_ci                    NULL, &lfs->rcache, lfs->cfg->block_size,
120319ea8026Sopenharmony_ci                    dir->pair[0], off+sizeof(tag),
120419ea8026Sopenharmony_ci                    lfs_tag_dsize(tag)-sizeof(tag), &crc);
120519ea8026Sopenharmony_ci            if (err) {
120619ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
120719ea8026Sopenharmony_ci                    break;
120819ea8026Sopenharmony_ci                }
120919ea8026Sopenharmony_ci                return err;
121019ea8026Sopenharmony_ci            }
121119ea8026Sopenharmony_ci
121219ea8026Sopenharmony_ci            // directory modification tags?
121319ea8026Sopenharmony_ci            if (lfs_tag_type1(tag) == LFS_TYPE_NAME) {
121419ea8026Sopenharmony_ci                // increase count of files if necessary
121519ea8026Sopenharmony_ci                if (lfs_tag_id(tag) >= tempcount) {
121619ea8026Sopenharmony_ci                    tempcount = lfs_tag_id(tag) + 1;
121719ea8026Sopenharmony_ci                }
121819ea8026Sopenharmony_ci            } else if (lfs_tag_type1(tag) == LFS_TYPE_SPLICE) {
121919ea8026Sopenharmony_ci                tempcount += lfs_tag_splice(tag);
122019ea8026Sopenharmony_ci
122119ea8026Sopenharmony_ci                if (tag == (LFS_MKTAG(LFS_TYPE_DELETE, 0, 0) |
122219ea8026Sopenharmony_ci                        (LFS_MKTAG(0, 0x3ff, 0) & tempbesttag))) {
122319ea8026Sopenharmony_ci                    tempbesttag |= 0x80000000;
122419ea8026Sopenharmony_ci                } else if (tempbesttag != -1 &&
122519ea8026Sopenharmony_ci                        lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) {
122619ea8026Sopenharmony_ci                    tempbesttag += LFS_MKTAG(0, lfs_tag_splice(tag), 0);
122719ea8026Sopenharmony_ci                }
122819ea8026Sopenharmony_ci            } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) {
122919ea8026Sopenharmony_ci                tempsplit = (lfs_tag_chunk(tag) & 1);
123019ea8026Sopenharmony_ci
123119ea8026Sopenharmony_ci                err = lfs_bd_read(lfs,
123219ea8026Sopenharmony_ci                        NULL, &lfs->rcache, lfs->cfg->block_size,
123319ea8026Sopenharmony_ci                        dir->pair[0], off+sizeof(tag), &temptail, 8);
123419ea8026Sopenharmony_ci                if (err) {
123519ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
123619ea8026Sopenharmony_ci                        break;
123719ea8026Sopenharmony_ci                    }
123819ea8026Sopenharmony_ci                    return err;
123919ea8026Sopenharmony_ci                }
124019ea8026Sopenharmony_ci                lfs_pair_fromle32(temptail);
124119ea8026Sopenharmony_ci            } else if (lfs_tag_type3(tag) == LFS_TYPE_FCRC) {
124219ea8026Sopenharmony_ci                err = lfs_bd_read(lfs,
124319ea8026Sopenharmony_ci                        NULL, &lfs->rcache, lfs->cfg->block_size,
124419ea8026Sopenharmony_ci                        dir->pair[0], off+sizeof(tag),
124519ea8026Sopenharmony_ci                        &fcrc, sizeof(fcrc));
124619ea8026Sopenharmony_ci                if (err) {
124719ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
124819ea8026Sopenharmony_ci                        break;
124919ea8026Sopenharmony_ci                    }
125019ea8026Sopenharmony_ci                }
125119ea8026Sopenharmony_ci
125219ea8026Sopenharmony_ci                lfs_fcrc_fromle32(&fcrc);
125319ea8026Sopenharmony_ci                hasfcrc = true;
125419ea8026Sopenharmony_ci            }
125519ea8026Sopenharmony_ci
125619ea8026Sopenharmony_ci            // found a match for our fetcher?
125719ea8026Sopenharmony_ci            if ((fmask & tag) == (fmask & ftag)) {
125819ea8026Sopenharmony_ci                int res = cb(data, tag, &(struct lfs_diskoff){
125919ea8026Sopenharmony_ci                        dir->pair[0], off+sizeof(tag)});
126019ea8026Sopenharmony_ci                if (res < 0) {
126119ea8026Sopenharmony_ci                    if (res == LFS_ERR_CORRUPT) {
126219ea8026Sopenharmony_ci                        break;
126319ea8026Sopenharmony_ci                    }
126419ea8026Sopenharmony_ci                    return res;
126519ea8026Sopenharmony_ci                }
126619ea8026Sopenharmony_ci
126719ea8026Sopenharmony_ci                if (res == LFS_CMP_EQ) {
126819ea8026Sopenharmony_ci                    // found a match
126919ea8026Sopenharmony_ci                    tempbesttag = tag;
127019ea8026Sopenharmony_ci                } else if ((LFS_MKTAG(0x7ff, 0x3ff, 0) & tag) ==
127119ea8026Sopenharmony_ci                        (LFS_MKTAG(0x7ff, 0x3ff, 0) & tempbesttag)) {
127219ea8026Sopenharmony_ci                    // found an identical tag, but contents didn't match
127319ea8026Sopenharmony_ci                    // this must mean that our besttag has been overwritten
127419ea8026Sopenharmony_ci                    tempbesttag = -1;
127519ea8026Sopenharmony_ci                } else if (res == LFS_CMP_GT &&
127619ea8026Sopenharmony_ci                        lfs_tag_id(tag) <= lfs_tag_id(tempbesttag)) {
127719ea8026Sopenharmony_ci                    // found a greater match, keep track to keep things sorted
127819ea8026Sopenharmony_ci                    tempbesttag = tag | 0x80000000;
127919ea8026Sopenharmony_ci                }
128019ea8026Sopenharmony_ci            }
128119ea8026Sopenharmony_ci        }
128219ea8026Sopenharmony_ci
128319ea8026Sopenharmony_ci        // found no valid commits?
128419ea8026Sopenharmony_ci        if (dir->off == 0) {
128519ea8026Sopenharmony_ci            // try the other block?
128619ea8026Sopenharmony_ci            lfs_pair_swap(dir->pair);
128719ea8026Sopenharmony_ci            dir->rev = revs[(r+1)%2];
128819ea8026Sopenharmony_ci            continue;
128919ea8026Sopenharmony_ci        }
129019ea8026Sopenharmony_ci
129119ea8026Sopenharmony_ci        // did we end on a valid commit? we may have an erased block
129219ea8026Sopenharmony_ci        dir->erased = false;
129319ea8026Sopenharmony_ci        if (maybeerased && dir->off % lfs->cfg->prog_size == 0) {
129419ea8026Sopenharmony_ci        #ifdef LFS_MULTIVERSION
129519ea8026Sopenharmony_ci            // note versions < lfs2.1 did not have fcrc tags, if
129619ea8026Sopenharmony_ci            // we're < lfs2.1 treat missing fcrc as erased data
129719ea8026Sopenharmony_ci            //
129819ea8026Sopenharmony_ci            // we don't strictly need to do this, but otherwise writing
129919ea8026Sopenharmony_ci            // to lfs2.0 disks becomes very inefficient
130019ea8026Sopenharmony_ci            if (lfs_fs_disk_version(lfs) < 0x00020001) {
130119ea8026Sopenharmony_ci                dir->erased = true;
130219ea8026Sopenharmony_ci
130319ea8026Sopenharmony_ci            } else
130419ea8026Sopenharmony_ci        #endif
130519ea8026Sopenharmony_ci            if (hasfcrc) {
130619ea8026Sopenharmony_ci                // check for an fcrc matching the next prog's erased state, if
130719ea8026Sopenharmony_ci                // this failed most likely a previous prog was interrupted, we
130819ea8026Sopenharmony_ci                // need a new erase
130919ea8026Sopenharmony_ci                uint32_t fcrc_ = 0xffffffff;
131019ea8026Sopenharmony_ci                int err = lfs_bd_crc(lfs,
131119ea8026Sopenharmony_ci                        NULL, &lfs->rcache, lfs->cfg->block_size,
131219ea8026Sopenharmony_ci                        dir->pair[0], dir->off, fcrc.size, &fcrc_);
131319ea8026Sopenharmony_ci                if (err && err != LFS_ERR_CORRUPT) {
131419ea8026Sopenharmony_ci                    return err;
131519ea8026Sopenharmony_ci                }
131619ea8026Sopenharmony_ci
131719ea8026Sopenharmony_ci                // found beginning of erased part?
131819ea8026Sopenharmony_ci                dir->erased = (fcrc_ == fcrc.crc);
131919ea8026Sopenharmony_ci            }
132019ea8026Sopenharmony_ci        }
132119ea8026Sopenharmony_ci
132219ea8026Sopenharmony_ci        // synthetic move
132319ea8026Sopenharmony_ci        if (lfs_gstate_hasmovehere(&lfs->gdisk, dir->pair)) {
132419ea8026Sopenharmony_ci            if (lfs_tag_id(lfs->gdisk.tag) == lfs_tag_id(besttag)) {
132519ea8026Sopenharmony_ci                besttag |= 0x80000000;
132619ea8026Sopenharmony_ci            } else if (besttag != -1 &&
132719ea8026Sopenharmony_ci                    lfs_tag_id(lfs->gdisk.tag) < lfs_tag_id(besttag)) {
132819ea8026Sopenharmony_ci                besttag -= LFS_MKTAG(0, 1, 0);
132919ea8026Sopenharmony_ci            }
133019ea8026Sopenharmony_ci        }
133119ea8026Sopenharmony_ci
133219ea8026Sopenharmony_ci        // found tag? or found best id?
133319ea8026Sopenharmony_ci        if (id) {
133419ea8026Sopenharmony_ci            *id = lfs_min(lfs_tag_id(besttag), dir->count);
133519ea8026Sopenharmony_ci        }
133619ea8026Sopenharmony_ci
133719ea8026Sopenharmony_ci        if (lfs_tag_isvalid(besttag)) {
133819ea8026Sopenharmony_ci            return besttag;
133919ea8026Sopenharmony_ci        } else if (lfs_tag_id(besttag) < dir->count) {
134019ea8026Sopenharmony_ci            return LFS_ERR_NOENT;
134119ea8026Sopenharmony_ci        } else {
134219ea8026Sopenharmony_ci            return 0;
134319ea8026Sopenharmony_ci        }
134419ea8026Sopenharmony_ci    }
134519ea8026Sopenharmony_ci
134619ea8026Sopenharmony_ci    LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}",
134719ea8026Sopenharmony_ci            dir->pair[0], dir->pair[1]);
134819ea8026Sopenharmony_ci    return LFS_ERR_CORRUPT;
134919ea8026Sopenharmony_ci}
135019ea8026Sopenharmony_ci
135119ea8026Sopenharmony_cistatic int lfs_dir_fetch(lfs_t *lfs,
135219ea8026Sopenharmony_ci        lfs_mdir_t *dir, const lfs_block_t pair[2]) {
135319ea8026Sopenharmony_ci    // note, mask=-1, tag=-1 can never match a tag since this
135419ea8026Sopenharmony_ci    // pattern has the invalid bit set
135519ea8026Sopenharmony_ci    return (int)lfs_dir_fetchmatch(lfs, dir, pair,
135619ea8026Sopenharmony_ci            (lfs_tag_t)-1, (lfs_tag_t)-1, NULL, NULL, NULL);
135719ea8026Sopenharmony_ci}
135819ea8026Sopenharmony_ci
135919ea8026Sopenharmony_cistatic int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir,
136019ea8026Sopenharmony_ci        lfs_gstate_t *gstate) {
136119ea8026Sopenharmony_ci    lfs_gstate_t temp;
136219ea8026Sopenharmony_ci    lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0),
136319ea8026Sopenharmony_ci            LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp);
136419ea8026Sopenharmony_ci    if (res < 0 && res != LFS_ERR_NOENT) {
136519ea8026Sopenharmony_ci        return res;
136619ea8026Sopenharmony_ci    }
136719ea8026Sopenharmony_ci
136819ea8026Sopenharmony_ci    if (res != LFS_ERR_NOENT) {
136919ea8026Sopenharmony_ci        // xor together to find resulting gstate
137019ea8026Sopenharmony_ci        lfs_gstate_fromle32(&temp);
137119ea8026Sopenharmony_ci        lfs_gstate_xor(gstate, &temp);
137219ea8026Sopenharmony_ci    }
137319ea8026Sopenharmony_ci
137419ea8026Sopenharmony_ci    return 0;
137519ea8026Sopenharmony_ci}
137619ea8026Sopenharmony_ci
137719ea8026Sopenharmony_cistatic int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
137819ea8026Sopenharmony_ci        uint16_t id, struct lfs_info *info) {
137919ea8026Sopenharmony_ci    if (id == 0x3ff) {
138019ea8026Sopenharmony_ci        // special case for root
138119ea8026Sopenharmony_ci        strcpy(info->name, "/");
138219ea8026Sopenharmony_ci        info->type = LFS_TYPE_DIR;
138319ea8026Sopenharmony_ci        return 0;
138419ea8026Sopenharmony_ci    }
138519ea8026Sopenharmony_ci
138619ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x780, 0x3ff, 0),
138719ea8026Sopenharmony_ci            LFS_MKTAG(LFS_TYPE_NAME, id, lfs->name_max+1), info->name);
138819ea8026Sopenharmony_ci    if (tag < 0) {
138919ea8026Sopenharmony_ci        return (int)tag;
139019ea8026Sopenharmony_ci    }
139119ea8026Sopenharmony_ci
139219ea8026Sopenharmony_ci    info->type = lfs_tag_type3(tag);
139319ea8026Sopenharmony_ci
139419ea8026Sopenharmony_ci    struct lfs_ctz ctz;
139519ea8026Sopenharmony_ci    tag = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0),
139619ea8026Sopenharmony_ci            LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz);
139719ea8026Sopenharmony_ci    if (tag < 0) {
139819ea8026Sopenharmony_ci        return (int)tag;
139919ea8026Sopenharmony_ci    }
140019ea8026Sopenharmony_ci    lfs_ctz_fromle32(&ctz);
140119ea8026Sopenharmony_ci
140219ea8026Sopenharmony_ci    if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) {
140319ea8026Sopenharmony_ci        info->size = ctz.size;
140419ea8026Sopenharmony_ci    } else if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) {
140519ea8026Sopenharmony_ci        info->size = lfs_tag_size(tag);
140619ea8026Sopenharmony_ci    }
140719ea8026Sopenharmony_ci
140819ea8026Sopenharmony_ci    return 0;
140919ea8026Sopenharmony_ci}
141019ea8026Sopenharmony_ci
141119ea8026Sopenharmony_cistruct lfs_dir_find_match {
141219ea8026Sopenharmony_ci    lfs_t *lfs;
141319ea8026Sopenharmony_ci    const void *name;
141419ea8026Sopenharmony_ci    lfs_size_t size;
141519ea8026Sopenharmony_ci};
141619ea8026Sopenharmony_ci
141719ea8026Sopenharmony_cistatic int lfs_dir_find_match(void *data,
141819ea8026Sopenharmony_ci        lfs_tag_t tag, const void *buffer) {
141919ea8026Sopenharmony_ci    struct lfs_dir_find_match *name = data;
142019ea8026Sopenharmony_ci    lfs_t *lfs = name->lfs;
142119ea8026Sopenharmony_ci    const struct lfs_diskoff *disk = buffer;
142219ea8026Sopenharmony_ci
142319ea8026Sopenharmony_ci    // compare with disk
142419ea8026Sopenharmony_ci    lfs_size_t diff = lfs_min(name->size, lfs_tag_size(tag));
142519ea8026Sopenharmony_ci    int res = lfs_bd_cmp(lfs,
142619ea8026Sopenharmony_ci            NULL, &lfs->rcache, diff,
142719ea8026Sopenharmony_ci            disk->block, disk->off, name->name, diff);
142819ea8026Sopenharmony_ci    if (res != LFS_CMP_EQ) {
142919ea8026Sopenharmony_ci        return res;
143019ea8026Sopenharmony_ci    }
143119ea8026Sopenharmony_ci
143219ea8026Sopenharmony_ci    // only equal if our size is still the same
143319ea8026Sopenharmony_ci    if (name->size != lfs_tag_size(tag)) {
143419ea8026Sopenharmony_ci        return (name->size < lfs_tag_size(tag)) ? LFS_CMP_LT : LFS_CMP_GT;
143519ea8026Sopenharmony_ci    }
143619ea8026Sopenharmony_ci
143719ea8026Sopenharmony_ci    // found a match!
143819ea8026Sopenharmony_ci    return LFS_CMP_EQ;
143919ea8026Sopenharmony_ci}
144019ea8026Sopenharmony_ci
144119ea8026Sopenharmony_cistatic lfs_stag_t lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
144219ea8026Sopenharmony_ci        const char **path, uint16_t *id) {
144319ea8026Sopenharmony_ci    // we reduce path to a single name if we can find it
144419ea8026Sopenharmony_ci    const char *name = *path;
144519ea8026Sopenharmony_ci    if (id) {
144619ea8026Sopenharmony_ci        *id = 0x3ff;
144719ea8026Sopenharmony_ci    }
144819ea8026Sopenharmony_ci
144919ea8026Sopenharmony_ci    // default to root dir
145019ea8026Sopenharmony_ci    lfs_stag_t tag = LFS_MKTAG(LFS_TYPE_DIR, 0x3ff, 0);
145119ea8026Sopenharmony_ci    dir->tail[0] = lfs->root[0];
145219ea8026Sopenharmony_ci    dir->tail[1] = lfs->root[1];
145319ea8026Sopenharmony_ci
145419ea8026Sopenharmony_ci    while (true) {
145519ea8026Sopenharmony_cinextname:
145619ea8026Sopenharmony_ci        // skip slashes
145719ea8026Sopenharmony_ci        name += strspn(name, "/");
145819ea8026Sopenharmony_ci        lfs_size_t namelen = strcspn(name, "/");
145919ea8026Sopenharmony_ci
146019ea8026Sopenharmony_ci        // skip '.' and root '..'
146119ea8026Sopenharmony_ci        if ((namelen == 1 && memcmp(name, ".", 1) == 0) ||
146219ea8026Sopenharmony_ci            (namelen == 2 && memcmp(name, "..", 2) == 0)) {
146319ea8026Sopenharmony_ci            name += namelen;
146419ea8026Sopenharmony_ci            goto nextname;
146519ea8026Sopenharmony_ci        }
146619ea8026Sopenharmony_ci
146719ea8026Sopenharmony_ci        // skip if matched by '..' in name
146819ea8026Sopenharmony_ci        const char *suffix = name + namelen;
146919ea8026Sopenharmony_ci        lfs_size_t sufflen;
147019ea8026Sopenharmony_ci        int depth = 1;
147119ea8026Sopenharmony_ci        while (true) {
147219ea8026Sopenharmony_ci            suffix += strspn(suffix, "/");
147319ea8026Sopenharmony_ci            sufflen = strcspn(suffix, "/");
147419ea8026Sopenharmony_ci            if (sufflen == 0) {
147519ea8026Sopenharmony_ci                break;
147619ea8026Sopenharmony_ci            }
147719ea8026Sopenharmony_ci
147819ea8026Sopenharmony_ci            if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
147919ea8026Sopenharmony_ci                depth -= 1;
148019ea8026Sopenharmony_ci                if (depth == 0) {
148119ea8026Sopenharmony_ci                    name = suffix + sufflen;
148219ea8026Sopenharmony_ci                    goto nextname;
148319ea8026Sopenharmony_ci                }
148419ea8026Sopenharmony_ci            } else {
148519ea8026Sopenharmony_ci                depth += 1;
148619ea8026Sopenharmony_ci            }
148719ea8026Sopenharmony_ci
148819ea8026Sopenharmony_ci            suffix += sufflen;
148919ea8026Sopenharmony_ci        }
149019ea8026Sopenharmony_ci
149119ea8026Sopenharmony_ci        // found path
149219ea8026Sopenharmony_ci        if (name[0] == '\0') {
149319ea8026Sopenharmony_ci            return tag;
149419ea8026Sopenharmony_ci        }
149519ea8026Sopenharmony_ci
149619ea8026Sopenharmony_ci        // update what we've found so far
149719ea8026Sopenharmony_ci        *path = name;
149819ea8026Sopenharmony_ci
149919ea8026Sopenharmony_ci        // only continue if we hit a directory
150019ea8026Sopenharmony_ci        if (lfs_tag_type3(tag) != LFS_TYPE_DIR) {
150119ea8026Sopenharmony_ci            return LFS_ERR_NOTDIR;
150219ea8026Sopenharmony_ci        }
150319ea8026Sopenharmony_ci
150419ea8026Sopenharmony_ci        // grab the entry data
150519ea8026Sopenharmony_ci        if (lfs_tag_id(tag) != 0x3ff) {
150619ea8026Sopenharmony_ci            lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0x3ff, 0),
150719ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), dir->tail);
150819ea8026Sopenharmony_ci            if (res < 0) {
150919ea8026Sopenharmony_ci                return res;
151019ea8026Sopenharmony_ci            }
151119ea8026Sopenharmony_ci            lfs_pair_fromle32(dir->tail);
151219ea8026Sopenharmony_ci        }
151319ea8026Sopenharmony_ci
151419ea8026Sopenharmony_ci        // find entry matching name
151519ea8026Sopenharmony_ci        while (true) {
151619ea8026Sopenharmony_ci            tag = lfs_dir_fetchmatch(lfs, dir, dir->tail,
151719ea8026Sopenharmony_ci                    LFS_MKTAG(0x780, 0, 0),
151819ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_NAME, 0, namelen),
151919ea8026Sopenharmony_ci                     // are we last name?
152019ea8026Sopenharmony_ci                    (strchr(name, '/') == NULL) ? id : NULL,
152119ea8026Sopenharmony_ci                    lfs_dir_find_match, &(struct lfs_dir_find_match){
152219ea8026Sopenharmony_ci                        lfs, name, namelen});
152319ea8026Sopenharmony_ci            if (tag < 0) {
152419ea8026Sopenharmony_ci                return tag;
152519ea8026Sopenharmony_ci            }
152619ea8026Sopenharmony_ci
152719ea8026Sopenharmony_ci            if (tag) {
152819ea8026Sopenharmony_ci                break;
152919ea8026Sopenharmony_ci            }
153019ea8026Sopenharmony_ci
153119ea8026Sopenharmony_ci            if (!dir->split) {
153219ea8026Sopenharmony_ci                return LFS_ERR_NOENT;
153319ea8026Sopenharmony_ci            }
153419ea8026Sopenharmony_ci        }
153519ea8026Sopenharmony_ci
153619ea8026Sopenharmony_ci        // to next name
153719ea8026Sopenharmony_ci        name += namelen;
153819ea8026Sopenharmony_ci    }
153919ea8026Sopenharmony_ci}
154019ea8026Sopenharmony_ci
154119ea8026Sopenharmony_ci// commit logic
154219ea8026Sopenharmony_cistruct lfs_commit {
154319ea8026Sopenharmony_ci    lfs_block_t block;
154419ea8026Sopenharmony_ci    lfs_off_t off;
154519ea8026Sopenharmony_ci    lfs_tag_t ptag;
154619ea8026Sopenharmony_ci    uint32_t crc;
154719ea8026Sopenharmony_ci
154819ea8026Sopenharmony_ci    lfs_off_t begin;
154919ea8026Sopenharmony_ci    lfs_off_t end;
155019ea8026Sopenharmony_ci};
155119ea8026Sopenharmony_ci
155219ea8026Sopenharmony_ci#ifndef LFS_READONLY
155319ea8026Sopenharmony_cistatic int lfs_dir_commitprog(lfs_t *lfs, struct lfs_commit *commit,
155419ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
155519ea8026Sopenharmony_ci    int err = lfs_bd_prog(lfs,
155619ea8026Sopenharmony_ci            &lfs->pcache, &lfs->rcache, false,
155719ea8026Sopenharmony_ci            commit->block, commit->off ,
155819ea8026Sopenharmony_ci            (const uint8_t*)buffer, size);
155919ea8026Sopenharmony_ci    if (err) {
156019ea8026Sopenharmony_ci        return err;
156119ea8026Sopenharmony_ci    }
156219ea8026Sopenharmony_ci
156319ea8026Sopenharmony_ci    commit->crc = lfs_crc(commit->crc, buffer, size);
156419ea8026Sopenharmony_ci    commit->off += size;
156519ea8026Sopenharmony_ci    return 0;
156619ea8026Sopenharmony_ci}
156719ea8026Sopenharmony_ci#endif
156819ea8026Sopenharmony_ci
156919ea8026Sopenharmony_ci#ifndef LFS_READONLY
157019ea8026Sopenharmony_cistatic int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit,
157119ea8026Sopenharmony_ci        lfs_tag_t tag, const void *buffer) {
157219ea8026Sopenharmony_ci    // check if we fit
157319ea8026Sopenharmony_ci    lfs_size_t dsize = lfs_tag_dsize(tag);
157419ea8026Sopenharmony_ci    if (commit->off + dsize > commit->end) {
157519ea8026Sopenharmony_ci        return LFS_ERR_NOSPC;
157619ea8026Sopenharmony_ci    }
157719ea8026Sopenharmony_ci
157819ea8026Sopenharmony_ci    // write out tag
157919ea8026Sopenharmony_ci    lfs_tag_t ntag = lfs_tobe32((tag & 0x7fffffff) ^ commit->ptag);
158019ea8026Sopenharmony_ci    int err = lfs_dir_commitprog(lfs, commit, &ntag, sizeof(ntag));
158119ea8026Sopenharmony_ci    if (err) {
158219ea8026Sopenharmony_ci        return err;
158319ea8026Sopenharmony_ci    }
158419ea8026Sopenharmony_ci
158519ea8026Sopenharmony_ci    if (!(tag & 0x80000000)) {
158619ea8026Sopenharmony_ci        // from memory
158719ea8026Sopenharmony_ci        err = lfs_dir_commitprog(lfs, commit, buffer, dsize-sizeof(tag));
158819ea8026Sopenharmony_ci        if (err) {
158919ea8026Sopenharmony_ci            return err;
159019ea8026Sopenharmony_ci        }
159119ea8026Sopenharmony_ci    } else {
159219ea8026Sopenharmony_ci        // from disk
159319ea8026Sopenharmony_ci        const struct lfs_diskoff *disk = buffer;
159419ea8026Sopenharmony_ci        for (lfs_off_t i = 0; i < dsize-sizeof(tag); i++) {
159519ea8026Sopenharmony_ci            // rely on caching to make this efficient
159619ea8026Sopenharmony_ci            uint8_t dat;
159719ea8026Sopenharmony_ci            err = lfs_bd_read(lfs,
159819ea8026Sopenharmony_ci                    NULL, &lfs->rcache, dsize-sizeof(tag)-i,
159919ea8026Sopenharmony_ci                    disk->block, disk->off+i, &dat, 1);
160019ea8026Sopenharmony_ci            if (err) {
160119ea8026Sopenharmony_ci                return err;
160219ea8026Sopenharmony_ci            }
160319ea8026Sopenharmony_ci
160419ea8026Sopenharmony_ci            err = lfs_dir_commitprog(lfs, commit, &dat, 1);
160519ea8026Sopenharmony_ci            if (err) {
160619ea8026Sopenharmony_ci                return err;
160719ea8026Sopenharmony_ci            }
160819ea8026Sopenharmony_ci        }
160919ea8026Sopenharmony_ci    }
161019ea8026Sopenharmony_ci
161119ea8026Sopenharmony_ci    commit->ptag = tag & 0x7fffffff;
161219ea8026Sopenharmony_ci    return 0;
161319ea8026Sopenharmony_ci}
161419ea8026Sopenharmony_ci#endif
161519ea8026Sopenharmony_ci
161619ea8026Sopenharmony_ci#ifndef LFS_READONLY
161719ea8026Sopenharmony_ci
161819ea8026Sopenharmony_cistatic int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) {
161919ea8026Sopenharmony_ci    // align to program units
162019ea8026Sopenharmony_ci    //
162119ea8026Sopenharmony_ci    // this gets a bit complex as we have two types of crcs:
162219ea8026Sopenharmony_ci    // - 5-word crc with fcrc to check following prog (middle of block)
162319ea8026Sopenharmony_ci    // - 2-word crc with no following prog (end of block)
162419ea8026Sopenharmony_ci    const lfs_off_t end = lfs_alignup(
162519ea8026Sopenharmony_ci            lfs_min(commit->off + 5*sizeof(uint32_t), lfs->cfg->block_size),
162619ea8026Sopenharmony_ci            lfs->cfg->prog_size);
162719ea8026Sopenharmony_ci
162819ea8026Sopenharmony_ci    lfs_off_t off1 = 0;
162919ea8026Sopenharmony_ci    uint32_t crc1 = 0;
163019ea8026Sopenharmony_ci
163119ea8026Sopenharmony_ci    // create crc tags to fill up remainder of commit, note that
163219ea8026Sopenharmony_ci    // padding is not crced, which lets fetches skip padding but
163319ea8026Sopenharmony_ci    // makes committing a bit more complicated
163419ea8026Sopenharmony_ci    while (commit->off < end) {
163519ea8026Sopenharmony_ci        lfs_off_t noff = (
163619ea8026Sopenharmony_ci                lfs_min(end - (commit->off+sizeof(lfs_tag_t)), 0x3fe)
163719ea8026Sopenharmony_ci                + (commit->off+sizeof(lfs_tag_t)));
163819ea8026Sopenharmony_ci        // too large for crc tag? need padding commits
163919ea8026Sopenharmony_ci        if (noff < end) {
164019ea8026Sopenharmony_ci            noff = lfs_min(noff, end - 5*sizeof(uint32_t));
164119ea8026Sopenharmony_ci        }
164219ea8026Sopenharmony_ci
164319ea8026Sopenharmony_ci        // space for fcrc?
164419ea8026Sopenharmony_ci        uint8_t eperturb = (uint8_t)-1;
164519ea8026Sopenharmony_ci        if (noff >= end && noff <= lfs->cfg->block_size - lfs->cfg->prog_size) {
164619ea8026Sopenharmony_ci            // first read the leading byte, this always contains a bit
164719ea8026Sopenharmony_ci            // we can perturb to avoid writes that don't change the fcrc
164819ea8026Sopenharmony_ci            int err = lfs_bd_read(lfs,
164919ea8026Sopenharmony_ci                    NULL, &lfs->rcache, lfs->cfg->prog_size,
165019ea8026Sopenharmony_ci                    commit->block, noff, &eperturb, 1);
165119ea8026Sopenharmony_ci            if (err && err != LFS_ERR_CORRUPT) {
165219ea8026Sopenharmony_ci                return err;
165319ea8026Sopenharmony_ci            }
165419ea8026Sopenharmony_ci
165519ea8026Sopenharmony_ci        #ifdef LFS_MULTIVERSION
165619ea8026Sopenharmony_ci            // unfortunately fcrcs break mdir fetching < lfs2.1, so only write
165719ea8026Sopenharmony_ci            // these if we're a >= lfs2.1 filesystem
165819ea8026Sopenharmony_ci            if (lfs_fs_disk_version(lfs) <= 0x00020000) {
165919ea8026Sopenharmony_ci                // don't write fcrc
166019ea8026Sopenharmony_ci            } else
166119ea8026Sopenharmony_ci        #endif
166219ea8026Sopenharmony_ci            {
166319ea8026Sopenharmony_ci                // find the expected fcrc, don't bother avoiding a reread
166419ea8026Sopenharmony_ci                // of the eperturb, it should still be in our cache
166519ea8026Sopenharmony_ci                struct lfs_fcrc fcrc = {
166619ea8026Sopenharmony_ci                    .size = lfs->cfg->prog_size,
166719ea8026Sopenharmony_ci                    .crc = 0xffffffff
166819ea8026Sopenharmony_ci                };
166919ea8026Sopenharmony_ci                err = lfs_bd_crc(lfs,
167019ea8026Sopenharmony_ci                        NULL, &lfs->rcache, lfs->cfg->prog_size,
167119ea8026Sopenharmony_ci                        commit->block, noff, fcrc.size, &fcrc.crc);
167219ea8026Sopenharmony_ci                if (err && err != LFS_ERR_CORRUPT) {
167319ea8026Sopenharmony_ci                    return err;
167419ea8026Sopenharmony_ci                }
167519ea8026Sopenharmony_ci
167619ea8026Sopenharmony_ci                lfs_fcrc_tole32(&fcrc);
167719ea8026Sopenharmony_ci                err = lfs_dir_commitattr(lfs, commit,
167819ea8026Sopenharmony_ci                        LFS_MKTAG(LFS_TYPE_FCRC, 0x3ff, sizeof(struct lfs_fcrc)),
167919ea8026Sopenharmony_ci                        &fcrc);
168019ea8026Sopenharmony_ci                if (err) {
168119ea8026Sopenharmony_ci                    return err;
168219ea8026Sopenharmony_ci                }
168319ea8026Sopenharmony_ci            }
168419ea8026Sopenharmony_ci        }
168519ea8026Sopenharmony_ci
168619ea8026Sopenharmony_ci        // build commit crc
168719ea8026Sopenharmony_ci        struct {
168819ea8026Sopenharmony_ci            lfs_tag_t tag;
168919ea8026Sopenharmony_ci            uint32_t crc;
169019ea8026Sopenharmony_ci        } ccrc;
169119ea8026Sopenharmony_ci        lfs_tag_t ntag = LFS_MKTAG(
169219ea8026Sopenharmony_ci                LFS_TYPE_CCRC + (((uint8_t)~eperturb) >> 7), 0x3ff,
169319ea8026Sopenharmony_ci                noff - (commit->off+sizeof(lfs_tag_t)));
169419ea8026Sopenharmony_ci        ccrc.tag = lfs_tobe32(ntag ^ commit->ptag);
169519ea8026Sopenharmony_ci        commit->crc = lfs_crc(commit->crc, &ccrc.tag, sizeof(lfs_tag_t));
169619ea8026Sopenharmony_ci        ccrc.crc = lfs_tole32(commit->crc);
169719ea8026Sopenharmony_ci
169819ea8026Sopenharmony_ci        int err = lfs_bd_prog(lfs,
169919ea8026Sopenharmony_ci                &lfs->pcache, &lfs->rcache, false,
170019ea8026Sopenharmony_ci                commit->block, commit->off, &ccrc, sizeof(ccrc));
170119ea8026Sopenharmony_ci        if (err) {
170219ea8026Sopenharmony_ci            return err;
170319ea8026Sopenharmony_ci        }
170419ea8026Sopenharmony_ci
170519ea8026Sopenharmony_ci        // keep track of non-padding checksum to verify
170619ea8026Sopenharmony_ci        if (off1 == 0) {
170719ea8026Sopenharmony_ci            off1 = commit->off + sizeof(lfs_tag_t);
170819ea8026Sopenharmony_ci            crc1 = commit->crc;
170919ea8026Sopenharmony_ci        }
171019ea8026Sopenharmony_ci
171119ea8026Sopenharmony_ci        commit->off = noff;
171219ea8026Sopenharmony_ci        // perturb valid bit?
171319ea8026Sopenharmony_ci        commit->ptag = ntag ^ ((0x80UL & ~eperturb) << 24);
171419ea8026Sopenharmony_ci        // reset crc for next commit
171519ea8026Sopenharmony_ci        commit->crc = 0xffffffff;
171619ea8026Sopenharmony_ci
171719ea8026Sopenharmony_ci        // manually flush here since we don't prog the padding, this confuses
171819ea8026Sopenharmony_ci        // the caching layer
171919ea8026Sopenharmony_ci        if (noff >= end || noff >= lfs->pcache.off + lfs->cfg->cache_size) {
172019ea8026Sopenharmony_ci            // flush buffers
172119ea8026Sopenharmony_ci            int err = lfs_bd_sync(lfs, &lfs->pcache, &lfs->rcache, false);
172219ea8026Sopenharmony_ci            if (err) {
172319ea8026Sopenharmony_ci                return err;
172419ea8026Sopenharmony_ci            }
172519ea8026Sopenharmony_ci        }
172619ea8026Sopenharmony_ci    }
172719ea8026Sopenharmony_ci
172819ea8026Sopenharmony_ci    // successful commit, check checksums to make sure
172919ea8026Sopenharmony_ci    //
173019ea8026Sopenharmony_ci    // note that we don't need to check padding commits, worst
173119ea8026Sopenharmony_ci    // case if they are corrupted we would have had to compact anyways
173219ea8026Sopenharmony_ci    lfs_off_t off = commit->begin;
173319ea8026Sopenharmony_ci    uint32_t crc = 0xffffffff;
173419ea8026Sopenharmony_ci    int err = lfs_bd_crc(lfs,
173519ea8026Sopenharmony_ci            NULL, &lfs->rcache, off1+sizeof(uint32_t),
173619ea8026Sopenharmony_ci            commit->block, off, off1-off, &crc);
173719ea8026Sopenharmony_ci    if (err) {
173819ea8026Sopenharmony_ci        return err;
173919ea8026Sopenharmony_ci    }
174019ea8026Sopenharmony_ci
174119ea8026Sopenharmony_ci    // check non-padding commits against known crc
174219ea8026Sopenharmony_ci    if (crc != crc1) {
174319ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
174419ea8026Sopenharmony_ci    }
174519ea8026Sopenharmony_ci
174619ea8026Sopenharmony_ci    // make sure to check crc in case we happen to pick
174719ea8026Sopenharmony_ci    // up an unrelated crc (frozen block?)
174819ea8026Sopenharmony_ci    err = lfs_bd_crc(lfs,
174919ea8026Sopenharmony_ci            NULL, &lfs->rcache, sizeof(uint32_t),
175019ea8026Sopenharmony_ci            commit->block, off1, sizeof(uint32_t), &crc);
175119ea8026Sopenharmony_ci    if (err) {
175219ea8026Sopenharmony_ci        return err;
175319ea8026Sopenharmony_ci    }
175419ea8026Sopenharmony_ci
175519ea8026Sopenharmony_ci    if (crc != 0) {
175619ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
175719ea8026Sopenharmony_ci    }
175819ea8026Sopenharmony_ci
175919ea8026Sopenharmony_ci    return 0;
176019ea8026Sopenharmony_ci}
176119ea8026Sopenharmony_ci#endif
176219ea8026Sopenharmony_ci
176319ea8026Sopenharmony_ci#ifndef LFS_READONLY
176419ea8026Sopenharmony_cistatic int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) {
176519ea8026Sopenharmony_ci    // allocate pair of dir blocks (backwards, so we write block 1 first)
176619ea8026Sopenharmony_ci    for (int i = 0; i < 2; i++) {
176719ea8026Sopenharmony_ci        int err = lfs_alloc(lfs, &dir->pair[(i+1)%2]);
176819ea8026Sopenharmony_ci        if (err) {
176919ea8026Sopenharmony_ci            return err;
177019ea8026Sopenharmony_ci        }
177119ea8026Sopenharmony_ci    }
177219ea8026Sopenharmony_ci
177319ea8026Sopenharmony_ci    // zero for reproducibility in case initial block is unreadable
177419ea8026Sopenharmony_ci    dir->rev = 0;
177519ea8026Sopenharmony_ci
177619ea8026Sopenharmony_ci    // rather than clobbering one of the blocks we just pretend
177719ea8026Sopenharmony_ci    // the revision may be valid
177819ea8026Sopenharmony_ci    int err = lfs_bd_read(lfs,
177919ea8026Sopenharmony_ci            NULL, &lfs->rcache, sizeof(dir->rev),
178019ea8026Sopenharmony_ci            dir->pair[0], 0, &dir->rev, sizeof(dir->rev));
178119ea8026Sopenharmony_ci    dir->rev = lfs_fromle32(dir->rev);
178219ea8026Sopenharmony_ci    if (err && err != LFS_ERR_CORRUPT) {
178319ea8026Sopenharmony_ci        return err;
178419ea8026Sopenharmony_ci    }
178519ea8026Sopenharmony_ci
178619ea8026Sopenharmony_ci    // to make sure we don't immediately evict, align the new revision count
178719ea8026Sopenharmony_ci    // to our block_cycles modulus, see lfs_dir_compact for why our modulus
178819ea8026Sopenharmony_ci    // is tweaked this way
178919ea8026Sopenharmony_ci    if (lfs->cfg->block_cycles > 0) {
179019ea8026Sopenharmony_ci        dir->rev = lfs_alignup(dir->rev, ((lfs->cfg->block_cycles+1)|1));
179119ea8026Sopenharmony_ci    }
179219ea8026Sopenharmony_ci
179319ea8026Sopenharmony_ci    // set defaults
179419ea8026Sopenharmony_ci    dir->off = sizeof(dir->rev);
179519ea8026Sopenharmony_ci    dir->etag = 0xffffffff;
179619ea8026Sopenharmony_ci    dir->count = 0;
179719ea8026Sopenharmony_ci    dir->tail[0] = LFS_BLOCK_NULL;
179819ea8026Sopenharmony_ci    dir->tail[1] = LFS_BLOCK_NULL;
179919ea8026Sopenharmony_ci    dir->erased = false;
180019ea8026Sopenharmony_ci    dir->split = false;
180119ea8026Sopenharmony_ci
180219ea8026Sopenharmony_ci    // don't write out yet, let caller take care of that
180319ea8026Sopenharmony_ci    return 0;
180419ea8026Sopenharmony_ci}
180519ea8026Sopenharmony_ci#endif
180619ea8026Sopenharmony_ci
180719ea8026Sopenharmony_ci#ifndef LFS_READONLY
180819ea8026Sopenharmony_cistatic int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) {
180919ea8026Sopenharmony_ci    // steal state
181019ea8026Sopenharmony_ci    int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta);
181119ea8026Sopenharmony_ci    if (err) {
181219ea8026Sopenharmony_ci        return err;
181319ea8026Sopenharmony_ci    }
181419ea8026Sopenharmony_ci
181519ea8026Sopenharmony_ci    // steal tail
181619ea8026Sopenharmony_ci    lfs_pair_tole32(tail->tail);
181719ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, dir, LFS_MKATTRS(
181819ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_TAIL + tail->split, 0x3ff, 8), tail->tail}));
181919ea8026Sopenharmony_ci    lfs_pair_fromle32(tail->tail);
182019ea8026Sopenharmony_ci    if (err) {
182119ea8026Sopenharmony_ci        return err;
182219ea8026Sopenharmony_ci    }
182319ea8026Sopenharmony_ci
182419ea8026Sopenharmony_ci    return 0;
182519ea8026Sopenharmony_ci}
182619ea8026Sopenharmony_ci#endif
182719ea8026Sopenharmony_ci
182819ea8026Sopenharmony_ci#ifndef LFS_READONLY
182919ea8026Sopenharmony_cistatic int lfs_dir_split(lfs_t *lfs,
183019ea8026Sopenharmony_ci        lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
183119ea8026Sopenharmony_ci        lfs_mdir_t *source, uint16_t split, uint16_t end) {
183219ea8026Sopenharmony_ci    // create tail metadata pair
183319ea8026Sopenharmony_ci    lfs_mdir_t tail;
183419ea8026Sopenharmony_ci    int err = lfs_dir_alloc(lfs, &tail);
183519ea8026Sopenharmony_ci    if (err) {
183619ea8026Sopenharmony_ci        return err;
183719ea8026Sopenharmony_ci    }
183819ea8026Sopenharmony_ci
183919ea8026Sopenharmony_ci    tail.split = dir->split;
184019ea8026Sopenharmony_ci    tail.tail[0] = dir->tail[0];
184119ea8026Sopenharmony_ci    tail.tail[1] = dir->tail[1];
184219ea8026Sopenharmony_ci
184319ea8026Sopenharmony_ci    // note we don't care about LFS_OK_RELOCATED
184419ea8026Sopenharmony_ci    int res = lfs_dir_compact(lfs, &tail, attrs, attrcount, source, split, end);
184519ea8026Sopenharmony_ci    if (res < 0) {
184619ea8026Sopenharmony_ci        return res;
184719ea8026Sopenharmony_ci    }
184819ea8026Sopenharmony_ci
184919ea8026Sopenharmony_ci    dir->tail[0] = tail.pair[0];
185019ea8026Sopenharmony_ci    dir->tail[1] = tail.pair[1];
185119ea8026Sopenharmony_ci    dir->split = true;
185219ea8026Sopenharmony_ci
185319ea8026Sopenharmony_ci    // update root if needed
185419ea8026Sopenharmony_ci    if (lfs_pair_cmp(dir->pair, lfs->root) == 0 && split == 0) {
185519ea8026Sopenharmony_ci        lfs->root[0] = tail.pair[0];
185619ea8026Sopenharmony_ci        lfs->root[1] = tail.pair[1];
185719ea8026Sopenharmony_ci    }
185819ea8026Sopenharmony_ci
185919ea8026Sopenharmony_ci    return 0;
186019ea8026Sopenharmony_ci}
186119ea8026Sopenharmony_ci#endif
186219ea8026Sopenharmony_ci
186319ea8026Sopenharmony_ci#ifndef LFS_READONLY
186419ea8026Sopenharmony_cistatic int lfs_dir_commit_size(void *p, lfs_tag_t tag, const void *buffer) {
186519ea8026Sopenharmony_ci    lfs_size_t *size = p;
186619ea8026Sopenharmony_ci    (void)buffer;
186719ea8026Sopenharmony_ci
186819ea8026Sopenharmony_ci    *size += lfs_tag_dsize(tag);
186919ea8026Sopenharmony_ci    return 0;
187019ea8026Sopenharmony_ci}
187119ea8026Sopenharmony_ci#endif
187219ea8026Sopenharmony_ci
187319ea8026Sopenharmony_ci#ifndef LFS_READONLY
187419ea8026Sopenharmony_cistruct lfs_dir_commit_commit {
187519ea8026Sopenharmony_ci    lfs_t *lfs;
187619ea8026Sopenharmony_ci    struct lfs_commit *commit;
187719ea8026Sopenharmony_ci};
187819ea8026Sopenharmony_ci#endif
187919ea8026Sopenharmony_ci
188019ea8026Sopenharmony_ci#ifndef LFS_READONLY
188119ea8026Sopenharmony_cistatic int lfs_dir_commit_commit(void *p, lfs_tag_t tag, const void *buffer) {
188219ea8026Sopenharmony_ci    struct lfs_dir_commit_commit *commit = p;
188319ea8026Sopenharmony_ci    return lfs_dir_commitattr(commit->lfs, commit->commit, tag, buffer);
188419ea8026Sopenharmony_ci}
188519ea8026Sopenharmony_ci#endif
188619ea8026Sopenharmony_ci
188719ea8026Sopenharmony_ci#ifndef LFS_READONLY
188819ea8026Sopenharmony_cistatic bool lfs_dir_needsrelocation(lfs_t *lfs, lfs_mdir_t *dir) {
188919ea8026Sopenharmony_ci    // If our revision count == n * block_cycles, we should force a relocation,
189019ea8026Sopenharmony_ci    // this is how littlefs wear-levels at the metadata-pair level. Note that we
189119ea8026Sopenharmony_ci    // actually use (block_cycles+1)|1, this is to avoid two corner cases:
189219ea8026Sopenharmony_ci    // 1. block_cycles = 1, which would prevent relocations from terminating
189319ea8026Sopenharmony_ci    // 2. block_cycles = 2n, which, due to aliasing, would only ever relocate
189419ea8026Sopenharmony_ci    //    one metadata block in the pair, effectively making this useless
189519ea8026Sopenharmony_ci    return (lfs->cfg->block_cycles > 0
189619ea8026Sopenharmony_ci            && ((dir->rev + 1) % ((lfs->cfg->block_cycles+1)|1) == 0));
189719ea8026Sopenharmony_ci}
189819ea8026Sopenharmony_ci#endif
189919ea8026Sopenharmony_ci
190019ea8026Sopenharmony_ci#ifndef LFS_READONLY
190119ea8026Sopenharmony_cistatic int lfs_dir_compact(lfs_t *lfs,
190219ea8026Sopenharmony_ci        lfs_mdir_t *dir, const struct lfs_mattr *attrs, int attrcount,
190319ea8026Sopenharmony_ci        lfs_mdir_t *source, uint16_t begin, uint16_t end) {
190419ea8026Sopenharmony_ci    // save some state in case block is bad
190519ea8026Sopenharmony_ci    bool relocated = false;
190619ea8026Sopenharmony_ci    bool tired = lfs_dir_needsrelocation(lfs, dir);
190719ea8026Sopenharmony_ci
190819ea8026Sopenharmony_ci    // increment revision count
190919ea8026Sopenharmony_ci    dir->rev += 1;
191019ea8026Sopenharmony_ci
191119ea8026Sopenharmony_ci    // do not proactively relocate blocks during migrations, this
191219ea8026Sopenharmony_ci    // can cause a number of failure states such: clobbering the
191319ea8026Sopenharmony_ci    // v1 superblock if we relocate root, and invalidating directory
191419ea8026Sopenharmony_ci    // pointers if we relocate the head of a directory. On top of
191519ea8026Sopenharmony_ci    // this, relocations increase the overall complexity of
191619ea8026Sopenharmony_ci    // lfs_migration, which is already a delicate operation.
191719ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
191819ea8026Sopenharmony_ci    if (lfs->lfs1) {
191919ea8026Sopenharmony_ci        tired = false;
192019ea8026Sopenharmony_ci    }
192119ea8026Sopenharmony_ci#endif
192219ea8026Sopenharmony_ci
192319ea8026Sopenharmony_ci    if (tired && lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) != 0) {
192419ea8026Sopenharmony_ci        // we're writing too much, time to relocate
192519ea8026Sopenharmony_ci        goto relocate;
192619ea8026Sopenharmony_ci    }
192719ea8026Sopenharmony_ci
192819ea8026Sopenharmony_ci    // begin loop to commit compaction to blocks until a compact sticks
192919ea8026Sopenharmony_ci    while (true) {
193019ea8026Sopenharmony_ci        {
193119ea8026Sopenharmony_ci            // setup commit state
193219ea8026Sopenharmony_ci            struct lfs_commit commit = {
193319ea8026Sopenharmony_ci                .block = dir->pair[1],
193419ea8026Sopenharmony_ci                .off = 0,
193519ea8026Sopenharmony_ci                .ptag = 0xffffffff,
193619ea8026Sopenharmony_ci                .crc = 0xffffffff,
193719ea8026Sopenharmony_ci
193819ea8026Sopenharmony_ci                .begin = 0,
193919ea8026Sopenharmony_ci                .end = (lfs->cfg->metadata_max ?
194019ea8026Sopenharmony_ci                    lfs->cfg->metadata_max : lfs->cfg->block_size) - 8,
194119ea8026Sopenharmony_ci            };
194219ea8026Sopenharmony_ci
194319ea8026Sopenharmony_ci            // erase block to write to
194419ea8026Sopenharmony_ci            int err = lfs_bd_erase(lfs, dir->pair[1]);
194519ea8026Sopenharmony_ci            if (err) {
194619ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
194719ea8026Sopenharmony_ci                    goto relocate;
194819ea8026Sopenharmony_ci                }
194919ea8026Sopenharmony_ci                return err;
195019ea8026Sopenharmony_ci            }
195119ea8026Sopenharmony_ci
195219ea8026Sopenharmony_ci            // write out header
195319ea8026Sopenharmony_ci            dir->rev = lfs_tole32(dir->rev);
195419ea8026Sopenharmony_ci            err = lfs_dir_commitprog(lfs, &commit,
195519ea8026Sopenharmony_ci                    &dir->rev, sizeof(dir->rev));
195619ea8026Sopenharmony_ci            dir->rev = lfs_fromle32(dir->rev);
195719ea8026Sopenharmony_ci            if (err) {
195819ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
195919ea8026Sopenharmony_ci                    goto relocate;
196019ea8026Sopenharmony_ci                }
196119ea8026Sopenharmony_ci                return err;
196219ea8026Sopenharmony_ci            }
196319ea8026Sopenharmony_ci
196419ea8026Sopenharmony_ci            // traverse the directory, this time writing out all unique tags
196519ea8026Sopenharmony_ci            err = lfs_dir_traverse(lfs,
196619ea8026Sopenharmony_ci                    source, 0, 0xffffffff, attrs, attrcount,
196719ea8026Sopenharmony_ci                    LFS_MKTAG(0x400, 0x3ff, 0),
196819ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
196919ea8026Sopenharmony_ci                    begin, end, -begin,
197019ea8026Sopenharmony_ci                    lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){
197119ea8026Sopenharmony_ci                        lfs, &commit});
197219ea8026Sopenharmony_ci            if (err) {
197319ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
197419ea8026Sopenharmony_ci                    goto relocate;
197519ea8026Sopenharmony_ci                }
197619ea8026Sopenharmony_ci                return err;
197719ea8026Sopenharmony_ci            }
197819ea8026Sopenharmony_ci
197919ea8026Sopenharmony_ci            // commit tail, which may be new after last size check
198019ea8026Sopenharmony_ci            if (!lfs_pair_isnull(dir->tail)) {
198119ea8026Sopenharmony_ci                lfs_pair_tole32(dir->tail);
198219ea8026Sopenharmony_ci                err = lfs_dir_commitattr(lfs, &commit,
198319ea8026Sopenharmony_ci                        LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8),
198419ea8026Sopenharmony_ci                        dir->tail);
198519ea8026Sopenharmony_ci                lfs_pair_fromle32(dir->tail);
198619ea8026Sopenharmony_ci                if (err) {
198719ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
198819ea8026Sopenharmony_ci                        goto relocate;
198919ea8026Sopenharmony_ci                    }
199019ea8026Sopenharmony_ci                    return err;
199119ea8026Sopenharmony_ci                }
199219ea8026Sopenharmony_ci            }
199319ea8026Sopenharmony_ci
199419ea8026Sopenharmony_ci            // bring over gstate?
199519ea8026Sopenharmony_ci            lfs_gstate_t delta = {0};
199619ea8026Sopenharmony_ci            if (!relocated) {
199719ea8026Sopenharmony_ci                lfs_gstate_xor(&delta, &lfs->gdisk);
199819ea8026Sopenharmony_ci                lfs_gstate_xor(&delta, &lfs->gstate);
199919ea8026Sopenharmony_ci            }
200019ea8026Sopenharmony_ci            lfs_gstate_xor(&delta, &lfs->gdelta);
200119ea8026Sopenharmony_ci            delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff);
200219ea8026Sopenharmony_ci
200319ea8026Sopenharmony_ci            err = lfs_dir_getgstate(lfs, dir, &delta);
200419ea8026Sopenharmony_ci            if (err) {
200519ea8026Sopenharmony_ci                return err;
200619ea8026Sopenharmony_ci            }
200719ea8026Sopenharmony_ci
200819ea8026Sopenharmony_ci            if (!lfs_gstate_iszero(&delta)) {
200919ea8026Sopenharmony_ci                lfs_gstate_tole32(&delta);
201019ea8026Sopenharmony_ci                err = lfs_dir_commitattr(lfs, &commit,
201119ea8026Sopenharmony_ci                        LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff,
201219ea8026Sopenharmony_ci                            sizeof(delta)), &delta);
201319ea8026Sopenharmony_ci                if (err) {
201419ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
201519ea8026Sopenharmony_ci                        goto relocate;
201619ea8026Sopenharmony_ci                    }
201719ea8026Sopenharmony_ci                    return err;
201819ea8026Sopenharmony_ci                }
201919ea8026Sopenharmony_ci            }
202019ea8026Sopenharmony_ci
202119ea8026Sopenharmony_ci            // complete commit with crc
202219ea8026Sopenharmony_ci            err = lfs_dir_commitcrc(lfs, &commit);
202319ea8026Sopenharmony_ci            if (err) {
202419ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
202519ea8026Sopenharmony_ci                    goto relocate;
202619ea8026Sopenharmony_ci                }
202719ea8026Sopenharmony_ci                return err;
202819ea8026Sopenharmony_ci            }
202919ea8026Sopenharmony_ci
203019ea8026Sopenharmony_ci            // successful compaction, swap dir pair to indicate most recent
203119ea8026Sopenharmony_ci            LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
203219ea8026Sopenharmony_ci            lfs_pair_swap(dir->pair);
203319ea8026Sopenharmony_ci            dir->count = end - begin;
203419ea8026Sopenharmony_ci            dir->off = commit.off;
203519ea8026Sopenharmony_ci            dir->etag = commit.ptag;
203619ea8026Sopenharmony_ci            // update gstate
203719ea8026Sopenharmony_ci            lfs->gdelta = (lfs_gstate_t){0};
203819ea8026Sopenharmony_ci            if (!relocated) {
203919ea8026Sopenharmony_ci                lfs->gdisk = lfs->gstate;
204019ea8026Sopenharmony_ci            }
204119ea8026Sopenharmony_ci        }
204219ea8026Sopenharmony_ci        break;
204319ea8026Sopenharmony_ci
204419ea8026Sopenharmony_cirelocate:
204519ea8026Sopenharmony_ci        // commit was corrupted, drop caches and prepare to relocate block
204619ea8026Sopenharmony_ci        relocated = true;
204719ea8026Sopenharmony_ci        lfs_cache_drop(lfs, &lfs->pcache);
204819ea8026Sopenharmony_ci        if (!tired) {
204919ea8026Sopenharmony_ci            LFS_DEBUG("Bad block at 0x%"PRIx32, dir->pair[1]);
205019ea8026Sopenharmony_ci        }
205119ea8026Sopenharmony_ci
205219ea8026Sopenharmony_ci        // can't relocate superblock, filesystem is now frozen
205319ea8026Sopenharmony_ci        if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
205419ea8026Sopenharmony_ci            LFS_WARN("Superblock 0x%"PRIx32" has become unwritable",
205519ea8026Sopenharmony_ci                    dir->pair[1]);
205619ea8026Sopenharmony_ci            return LFS_ERR_NOSPC;
205719ea8026Sopenharmony_ci        }
205819ea8026Sopenharmony_ci
205919ea8026Sopenharmony_ci        // relocate half of pair
206019ea8026Sopenharmony_ci        int err = lfs_alloc(lfs, &dir->pair[1]);
206119ea8026Sopenharmony_ci        if (err && (err != LFS_ERR_NOSPC || !tired)) {
206219ea8026Sopenharmony_ci            return err;
206319ea8026Sopenharmony_ci        }
206419ea8026Sopenharmony_ci
206519ea8026Sopenharmony_ci        tired = false;
206619ea8026Sopenharmony_ci        continue;
206719ea8026Sopenharmony_ci    }
206819ea8026Sopenharmony_ci
206919ea8026Sopenharmony_ci    return relocated ? LFS_OK_RELOCATED : 0;
207019ea8026Sopenharmony_ci}
207119ea8026Sopenharmony_ci#endif
207219ea8026Sopenharmony_ci
207319ea8026Sopenharmony_ci#ifndef LFS_READONLY
207419ea8026Sopenharmony_cistatic int lfs_dir_splittingcompact(lfs_t *lfs, lfs_mdir_t *dir,
207519ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount,
207619ea8026Sopenharmony_ci        lfs_mdir_t *source, uint16_t begin, uint16_t end) {
207719ea8026Sopenharmony_ci    while (true) {
207819ea8026Sopenharmony_ci        // find size of first split, we do this by halving the split until
207919ea8026Sopenharmony_ci        // the metadata is guaranteed to fit
208019ea8026Sopenharmony_ci        //
208119ea8026Sopenharmony_ci        // Note that this isn't a true binary search, we never increase the
208219ea8026Sopenharmony_ci        // split size. This may result in poorly distributed metadata but isn't
208319ea8026Sopenharmony_ci        // worth the extra code size or performance hit to fix.
208419ea8026Sopenharmony_ci        lfs_size_t split = begin;
208519ea8026Sopenharmony_ci        while (end - split > 1) {
208619ea8026Sopenharmony_ci            lfs_size_t size = 0;
208719ea8026Sopenharmony_ci            int err = lfs_dir_traverse(lfs,
208819ea8026Sopenharmony_ci                    source, 0, 0xffffffff, attrs, attrcount,
208919ea8026Sopenharmony_ci                    LFS_MKTAG(0x400, 0x3ff, 0),
209019ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_NAME, 0, 0),
209119ea8026Sopenharmony_ci                    split, end, -split,
209219ea8026Sopenharmony_ci                    lfs_dir_commit_size, &size);
209319ea8026Sopenharmony_ci            if (err) {
209419ea8026Sopenharmony_ci                return err;
209519ea8026Sopenharmony_ci            }
209619ea8026Sopenharmony_ci
209719ea8026Sopenharmony_ci            // space is complicated, we need room for:
209819ea8026Sopenharmony_ci            //
209919ea8026Sopenharmony_ci            // - tail:         4+2*4 = 12 bytes
210019ea8026Sopenharmony_ci            // - gstate:       4+3*4 = 16 bytes
210119ea8026Sopenharmony_ci            // - move delete:  4     = 4 bytes
210219ea8026Sopenharmony_ci            // - crc:          4+4   = 8 bytes
210319ea8026Sopenharmony_ci            //                 total = 40 bytes
210419ea8026Sopenharmony_ci            //
210519ea8026Sopenharmony_ci            // And we cap at half a block to avoid degenerate cases with
210619ea8026Sopenharmony_ci            // nearly-full metadata blocks.
210719ea8026Sopenharmony_ci            //
210819ea8026Sopenharmony_ci            if (end - split < 0xff
210919ea8026Sopenharmony_ci                    && size <= lfs_min(
211019ea8026Sopenharmony_ci                        lfs->cfg->block_size - 40,
211119ea8026Sopenharmony_ci                        lfs_alignup(
211219ea8026Sopenharmony_ci                            (lfs->cfg->metadata_max
211319ea8026Sopenharmony_ci                                ? lfs->cfg->metadata_max
211419ea8026Sopenharmony_ci                                : lfs->cfg->block_size)/2,
211519ea8026Sopenharmony_ci                            lfs->cfg->prog_size))) {
211619ea8026Sopenharmony_ci                break;
211719ea8026Sopenharmony_ci            }
211819ea8026Sopenharmony_ci
211919ea8026Sopenharmony_ci            split = split + ((end - split) / 2);
212019ea8026Sopenharmony_ci        }
212119ea8026Sopenharmony_ci
212219ea8026Sopenharmony_ci        if (split == begin) {
212319ea8026Sopenharmony_ci            // no split needed
212419ea8026Sopenharmony_ci            break;
212519ea8026Sopenharmony_ci        }
212619ea8026Sopenharmony_ci
212719ea8026Sopenharmony_ci        // split into two metadata pairs and continue
212819ea8026Sopenharmony_ci        int err = lfs_dir_split(lfs, dir, attrs, attrcount,
212919ea8026Sopenharmony_ci                source, split, end);
213019ea8026Sopenharmony_ci        if (err && err != LFS_ERR_NOSPC) {
213119ea8026Sopenharmony_ci            return err;
213219ea8026Sopenharmony_ci        }
213319ea8026Sopenharmony_ci
213419ea8026Sopenharmony_ci        if (err) {
213519ea8026Sopenharmony_ci            // we can't allocate a new block, try to compact with degraded
213619ea8026Sopenharmony_ci            // performance
213719ea8026Sopenharmony_ci            LFS_WARN("Unable to split {0x%"PRIx32", 0x%"PRIx32"}",
213819ea8026Sopenharmony_ci                    dir->pair[0], dir->pair[1]);
213919ea8026Sopenharmony_ci            break;
214019ea8026Sopenharmony_ci        } else {
214119ea8026Sopenharmony_ci            end = split;
214219ea8026Sopenharmony_ci        }
214319ea8026Sopenharmony_ci    }
214419ea8026Sopenharmony_ci
214519ea8026Sopenharmony_ci    if (lfs_dir_needsrelocation(lfs, dir)
214619ea8026Sopenharmony_ci            && lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) {
214719ea8026Sopenharmony_ci        // oh no! we're writing too much to the superblock,
214819ea8026Sopenharmony_ci        // should we expand?
214919ea8026Sopenharmony_ci        lfs_ssize_t size = lfs_fs_rawsize(lfs);
215019ea8026Sopenharmony_ci        if (size < 0) {
215119ea8026Sopenharmony_ci            return size;
215219ea8026Sopenharmony_ci        }
215319ea8026Sopenharmony_ci
215419ea8026Sopenharmony_ci        // do we have extra space? littlefs can't reclaim this space
215519ea8026Sopenharmony_ci        // by itself, so expand cautiously
215619ea8026Sopenharmony_ci        if ((lfs_size_t)size < lfs->block_count/2) {
215719ea8026Sopenharmony_ci            LFS_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev);
215819ea8026Sopenharmony_ci            int err = lfs_dir_split(lfs, dir, attrs, attrcount,
215919ea8026Sopenharmony_ci                    source, begin, end);
216019ea8026Sopenharmony_ci            if (err && err != LFS_ERR_NOSPC) {
216119ea8026Sopenharmony_ci                return err;
216219ea8026Sopenharmony_ci            }
216319ea8026Sopenharmony_ci
216419ea8026Sopenharmony_ci            if (err) {
216519ea8026Sopenharmony_ci                // welp, we tried, if we ran out of space there's not much
216619ea8026Sopenharmony_ci                // we can do, we'll error later if we've become frozen
216719ea8026Sopenharmony_ci                LFS_WARN("Unable to expand superblock");
216819ea8026Sopenharmony_ci            } else {
216919ea8026Sopenharmony_ci                end = begin;
217019ea8026Sopenharmony_ci            }
217119ea8026Sopenharmony_ci        }
217219ea8026Sopenharmony_ci    }
217319ea8026Sopenharmony_ci
217419ea8026Sopenharmony_ci    return lfs_dir_compact(lfs, dir, attrs, attrcount, source, begin, end);
217519ea8026Sopenharmony_ci}
217619ea8026Sopenharmony_ci#endif
217719ea8026Sopenharmony_ci
217819ea8026Sopenharmony_ci#ifndef LFS_READONLY
217919ea8026Sopenharmony_cistatic int lfs_dir_relocatingcommit(lfs_t *lfs, lfs_mdir_t *dir,
218019ea8026Sopenharmony_ci        const lfs_block_t pair[2],
218119ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount,
218219ea8026Sopenharmony_ci        lfs_mdir_t *pdir) {
218319ea8026Sopenharmony_ci    int state = 0;
218419ea8026Sopenharmony_ci
218519ea8026Sopenharmony_ci    // calculate changes to the directory
218619ea8026Sopenharmony_ci    bool hasdelete = false;
218719ea8026Sopenharmony_ci    for (int i = 0; i < attrcount; i++) {
218819ea8026Sopenharmony_ci        if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) {
218919ea8026Sopenharmony_ci            dir->count += 1;
219019ea8026Sopenharmony_ci        } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE) {
219119ea8026Sopenharmony_ci            LFS_ASSERT(dir->count > 0);
219219ea8026Sopenharmony_ci            dir->count -= 1;
219319ea8026Sopenharmony_ci            hasdelete = true;
219419ea8026Sopenharmony_ci        } else if (lfs_tag_type1(attrs[i].tag) == LFS_TYPE_TAIL) {
219519ea8026Sopenharmony_ci            dir->tail[0] = ((lfs_block_t*)attrs[i].buffer)[0];
219619ea8026Sopenharmony_ci            dir->tail[1] = ((lfs_block_t*)attrs[i].buffer)[1];
219719ea8026Sopenharmony_ci            dir->split = (lfs_tag_chunk(attrs[i].tag) & 1);
219819ea8026Sopenharmony_ci            lfs_pair_fromle32(dir->tail);
219919ea8026Sopenharmony_ci        }
220019ea8026Sopenharmony_ci    }
220119ea8026Sopenharmony_ci
220219ea8026Sopenharmony_ci    // should we actually drop the directory block?
220319ea8026Sopenharmony_ci    if (hasdelete && dir->count == 0) {
220419ea8026Sopenharmony_ci        LFS_ASSERT(pdir);
220519ea8026Sopenharmony_ci        int err = lfs_fs_pred(lfs, dir->pair, pdir);
220619ea8026Sopenharmony_ci        if (err && err != LFS_ERR_NOENT) {
220719ea8026Sopenharmony_ci            return err;
220819ea8026Sopenharmony_ci        }
220919ea8026Sopenharmony_ci
221019ea8026Sopenharmony_ci        if (err != LFS_ERR_NOENT && pdir->split) {
221119ea8026Sopenharmony_ci            state = LFS_OK_DROPPED;
221219ea8026Sopenharmony_ci            goto fixmlist;
221319ea8026Sopenharmony_ci        }
221419ea8026Sopenharmony_ci    }
221519ea8026Sopenharmony_ci
221619ea8026Sopenharmony_ci    if (dir->erased) {
221719ea8026Sopenharmony_ci        // try to commit
221819ea8026Sopenharmony_ci        struct lfs_commit commit = {
221919ea8026Sopenharmony_ci            .block = dir->pair[0],
222019ea8026Sopenharmony_ci            .off = dir->off,
222119ea8026Sopenharmony_ci            .ptag = dir->etag,
222219ea8026Sopenharmony_ci            .crc = 0xffffffff,
222319ea8026Sopenharmony_ci
222419ea8026Sopenharmony_ci            .begin = dir->off,
222519ea8026Sopenharmony_ci            .end = (lfs->cfg->metadata_max ?
222619ea8026Sopenharmony_ci                lfs->cfg->metadata_max : lfs->cfg->block_size) - 8,
222719ea8026Sopenharmony_ci        };
222819ea8026Sopenharmony_ci
222919ea8026Sopenharmony_ci        // traverse attrs that need to be written out
223019ea8026Sopenharmony_ci        lfs_pair_tole32(dir->tail);
223119ea8026Sopenharmony_ci        int err = lfs_dir_traverse(lfs,
223219ea8026Sopenharmony_ci                dir, dir->off, dir->etag, attrs, attrcount,
223319ea8026Sopenharmony_ci                0, 0, 0, 0, 0,
223419ea8026Sopenharmony_ci                lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){
223519ea8026Sopenharmony_ci                    lfs, &commit});
223619ea8026Sopenharmony_ci        lfs_pair_fromle32(dir->tail);
223719ea8026Sopenharmony_ci        if (err) {
223819ea8026Sopenharmony_ci            if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
223919ea8026Sopenharmony_ci                goto compact;
224019ea8026Sopenharmony_ci            }
224119ea8026Sopenharmony_ci            return err;
224219ea8026Sopenharmony_ci        }
224319ea8026Sopenharmony_ci
224419ea8026Sopenharmony_ci        // commit any global diffs if we have any
224519ea8026Sopenharmony_ci        lfs_gstate_t delta = {0};
224619ea8026Sopenharmony_ci        lfs_gstate_xor(&delta, &lfs->gstate);
224719ea8026Sopenharmony_ci        lfs_gstate_xor(&delta, &lfs->gdisk);
224819ea8026Sopenharmony_ci        lfs_gstate_xor(&delta, &lfs->gdelta);
224919ea8026Sopenharmony_ci        delta.tag &= ~LFS_MKTAG(0, 0, 0x3ff);
225019ea8026Sopenharmony_ci        if (!lfs_gstate_iszero(&delta)) {
225119ea8026Sopenharmony_ci            err = lfs_dir_getgstate(lfs, dir, &delta);
225219ea8026Sopenharmony_ci            if (err) {
225319ea8026Sopenharmony_ci                return err;
225419ea8026Sopenharmony_ci            }
225519ea8026Sopenharmony_ci
225619ea8026Sopenharmony_ci            lfs_gstate_tole32(&delta);
225719ea8026Sopenharmony_ci            err = lfs_dir_commitattr(lfs, &commit,
225819ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff,
225919ea8026Sopenharmony_ci                        sizeof(delta)), &delta);
226019ea8026Sopenharmony_ci            if (err) {
226119ea8026Sopenharmony_ci                if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
226219ea8026Sopenharmony_ci                    goto compact;
226319ea8026Sopenharmony_ci                }
226419ea8026Sopenharmony_ci                return err;
226519ea8026Sopenharmony_ci            }
226619ea8026Sopenharmony_ci        }
226719ea8026Sopenharmony_ci
226819ea8026Sopenharmony_ci        // finalize commit with the crc
226919ea8026Sopenharmony_ci        err = lfs_dir_commitcrc(lfs, &commit);
227019ea8026Sopenharmony_ci        if (err) {
227119ea8026Sopenharmony_ci            if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) {
227219ea8026Sopenharmony_ci                goto compact;
227319ea8026Sopenharmony_ci            }
227419ea8026Sopenharmony_ci            return err;
227519ea8026Sopenharmony_ci        }
227619ea8026Sopenharmony_ci
227719ea8026Sopenharmony_ci        // successful commit, update dir
227819ea8026Sopenharmony_ci        LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0);
227919ea8026Sopenharmony_ci        dir->off = commit.off;
228019ea8026Sopenharmony_ci        dir->etag = commit.ptag;
228119ea8026Sopenharmony_ci        // and update gstate
228219ea8026Sopenharmony_ci        lfs->gdisk = lfs->gstate;
228319ea8026Sopenharmony_ci        lfs->gdelta = (lfs_gstate_t){0};
228419ea8026Sopenharmony_ci
228519ea8026Sopenharmony_ci        goto fixmlist;
228619ea8026Sopenharmony_ci    }
228719ea8026Sopenharmony_ci
228819ea8026Sopenharmony_cicompact:
228919ea8026Sopenharmony_ci    // fall back to compaction
229019ea8026Sopenharmony_ci    lfs_cache_drop(lfs, &lfs->pcache);
229119ea8026Sopenharmony_ci
229219ea8026Sopenharmony_ci    state = lfs_dir_splittingcompact(lfs, dir, attrs, attrcount,
229319ea8026Sopenharmony_ci            dir, 0, dir->count);
229419ea8026Sopenharmony_ci    if (state < 0) {
229519ea8026Sopenharmony_ci        return state;
229619ea8026Sopenharmony_ci    }
229719ea8026Sopenharmony_ci
229819ea8026Sopenharmony_ci    goto fixmlist;
229919ea8026Sopenharmony_ci
230019ea8026Sopenharmony_cifixmlist:;
230119ea8026Sopenharmony_ci    // this complicated bit of logic is for fixing up any active
230219ea8026Sopenharmony_ci    // metadata-pairs that we may have affected
230319ea8026Sopenharmony_ci    //
230419ea8026Sopenharmony_ci    // note we have to make two passes since the mdir passed to
230519ea8026Sopenharmony_ci    // lfs_dir_commit could also be in this list, and even then
230619ea8026Sopenharmony_ci    // we need to copy the pair so they don't get clobbered if we refetch
230719ea8026Sopenharmony_ci    // our mdir.
230819ea8026Sopenharmony_ci    lfs_block_t oldpair[2] = {pair[0], pair[1]};
230919ea8026Sopenharmony_ci    for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
231019ea8026Sopenharmony_ci        if (lfs_pair_cmp(d->m.pair, oldpair) == 0) {
231119ea8026Sopenharmony_ci            d->m = *dir;
231219ea8026Sopenharmony_ci            if (d->m.pair != pair) {
231319ea8026Sopenharmony_ci                for (int i = 0; i < attrcount; i++) {
231419ea8026Sopenharmony_ci                    if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
231519ea8026Sopenharmony_ci                            d->id == lfs_tag_id(attrs[i].tag)) {
231619ea8026Sopenharmony_ci                        d->m.pair[0] = LFS_BLOCK_NULL;
231719ea8026Sopenharmony_ci                        d->m.pair[1] = LFS_BLOCK_NULL;
231819ea8026Sopenharmony_ci                    } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_DELETE &&
231919ea8026Sopenharmony_ci                            d->id > lfs_tag_id(attrs[i].tag)) {
232019ea8026Sopenharmony_ci                        d->id -= 1;
232119ea8026Sopenharmony_ci                        if (d->type == LFS_TYPE_DIR) {
232219ea8026Sopenharmony_ci                            ((lfs_dir_t*)d)->pos -= 1;
232319ea8026Sopenharmony_ci                        }
232419ea8026Sopenharmony_ci                    } else if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE &&
232519ea8026Sopenharmony_ci                            d->id >= lfs_tag_id(attrs[i].tag)) {
232619ea8026Sopenharmony_ci                        d->id += 1;
232719ea8026Sopenharmony_ci                        if (d->type == LFS_TYPE_DIR) {
232819ea8026Sopenharmony_ci                            ((lfs_dir_t*)d)->pos += 1;
232919ea8026Sopenharmony_ci                        }
233019ea8026Sopenharmony_ci                    }
233119ea8026Sopenharmony_ci                }
233219ea8026Sopenharmony_ci            }
233319ea8026Sopenharmony_ci
233419ea8026Sopenharmony_ci            while (d->id >= d->m.count && d->m.split) {
233519ea8026Sopenharmony_ci                // we split and id is on tail now
233619ea8026Sopenharmony_ci                d->id -= d->m.count;
233719ea8026Sopenharmony_ci                int err = lfs_dir_fetch(lfs, &d->m, d->m.tail);
233819ea8026Sopenharmony_ci                if (err) {
233919ea8026Sopenharmony_ci                    return err;
234019ea8026Sopenharmony_ci                }
234119ea8026Sopenharmony_ci            }
234219ea8026Sopenharmony_ci        }
234319ea8026Sopenharmony_ci    }
234419ea8026Sopenharmony_ci
234519ea8026Sopenharmony_ci    return state;
234619ea8026Sopenharmony_ci}
234719ea8026Sopenharmony_ci#endif
234819ea8026Sopenharmony_ci
234919ea8026Sopenharmony_ci#ifndef LFS_READONLY
235019ea8026Sopenharmony_cistatic int lfs_dir_orphaningcommit(lfs_t *lfs, lfs_mdir_t *dir,
235119ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount) {
235219ea8026Sopenharmony_ci    // check for any inline files that aren't RAM backed and
235319ea8026Sopenharmony_ci    // forcefully evict them, needed for filesystem consistency
235419ea8026Sopenharmony_ci    for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
235519ea8026Sopenharmony_ci        if (dir != &f->m && lfs_pair_cmp(f->m.pair, dir->pair) == 0 &&
235619ea8026Sopenharmony_ci                f->type == LFS_TYPE_REG && (f->flags & LFS_F_INLINE) &&
235719ea8026Sopenharmony_ci                f->ctz.size > lfs->cfg->cache_size) {
235819ea8026Sopenharmony_ci            int err = lfs_file_outline(lfs, f);
235919ea8026Sopenharmony_ci            if (err) {
236019ea8026Sopenharmony_ci                return err;
236119ea8026Sopenharmony_ci            }
236219ea8026Sopenharmony_ci
236319ea8026Sopenharmony_ci            err = lfs_file_flush(lfs, f);
236419ea8026Sopenharmony_ci            if (err) {
236519ea8026Sopenharmony_ci                return err;
236619ea8026Sopenharmony_ci            }
236719ea8026Sopenharmony_ci        }
236819ea8026Sopenharmony_ci    }
236919ea8026Sopenharmony_ci
237019ea8026Sopenharmony_ci    lfs_block_t lpair[2] = {dir->pair[0], dir->pair[1]};
237119ea8026Sopenharmony_ci    lfs_mdir_t ldir = *dir;
237219ea8026Sopenharmony_ci    lfs_mdir_t pdir;
237319ea8026Sopenharmony_ci    int state = lfs_dir_relocatingcommit(lfs, &ldir, dir->pair,
237419ea8026Sopenharmony_ci            attrs, attrcount, &pdir);
237519ea8026Sopenharmony_ci    if (state < 0) {
237619ea8026Sopenharmony_ci        return state;
237719ea8026Sopenharmony_ci    }
237819ea8026Sopenharmony_ci
237919ea8026Sopenharmony_ci    // update if we're not in mlist, note we may have already been
238019ea8026Sopenharmony_ci    // updated if we are in mlist
238119ea8026Sopenharmony_ci    if (lfs_pair_cmp(dir->pair, lpair) == 0) {
238219ea8026Sopenharmony_ci        *dir = ldir;
238319ea8026Sopenharmony_ci    }
238419ea8026Sopenharmony_ci
238519ea8026Sopenharmony_ci    // commit was successful, but may require other changes in the
238619ea8026Sopenharmony_ci    // filesystem, these would normally be tail recursive, but we have
238719ea8026Sopenharmony_ci    // flattened them here avoid unbounded stack usage
238819ea8026Sopenharmony_ci
238919ea8026Sopenharmony_ci    // need to drop?
239019ea8026Sopenharmony_ci    if (state == LFS_OK_DROPPED) {
239119ea8026Sopenharmony_ci        // steal state
239219ea8026Sopenharmony_ci        int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta);
239319ea8026Sopenharmony_ci        if (err) {
239419ea8026Sopenharmony_ci            return err;
239519ea8026Sopenharmony_ci        }
239619ea8026Sopenharmony_ci
239719ea8026Sopenharmony_ci        // steal tail, note that this can't create a recursive drop
239819ea8026Sopenharmony_ci        lpair[0] = pdir.pair[0];
239919ea8026Sopenharmony_ci        lpair[1] = pdir.pair[1];
240019ea8026Sopenharmony_ci        lfs_pair_tole32(dir->tail);
240119ea8026Sopenharmony_ci        state = lfs_dir_relocatingcommit(lfs, &pdir, lpair, LFS_MKATTRS(
240219ea8026Sopenharmony_ci                    {LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8),
240319ea8026Sopenharmony_ci                        dir->tail}),
240419ea8026Sopenharmony_ci                NULL);
240519ea8026Sopenharmony_ci        lfs_pair_fromle32(dir->tail);
240619ea8026Sopenharmony_ci        if (state < 0) {
240719ea8026Sopenharmony_ci            return state;
240819ea8026Sopenharmony_ci        }
240919ea8026Sopenharmony_ci
241019ea8026Sopenharmony_ci        ldir = pdir;
241119ea8026Sopenharmony_ci    }
241219ea8026Sopenharmony_ci
241319ea8026Sopenharmony_ci    // need to relocate?
241419ea8026Sopenharmony_ci    bool orphans = false;
241519ea8026Sopenharmony_ci    while (state == LFS_OK_RELOCATED) {
241619ea8026Sopenharmony_ci        LFS_DEBUG("Relocating {0x%"PRIx32", 0x%"PRIx32"} "
241719ea8026Sopenharmony_ci                    "-> {0x%"PRIx32", 0x%"PRIx32"}",
241819ea8026Sopenharmony_ci                lpair[0], lpair[1], ldir.pair[0], ldir.pair[1]);
241919ea8026Sopenharmony_ci        state = 0;
242019ea8026Sopenharmony_ci
242119ea8026Sopenharmony_ci        // update internal root
242219ea8026Sopenharmony_ci        if (lfs_pair_cmp(lpair, lfs->root) == 0) {
242319ea8026Sopenharmony_ci            lfs->root[0] = ldir.pair[0];
242419ea8026Sopenharmony_ci            lfs->root[1] = ldir.pair[1];
242519ea8026Sopenharmony_ci        }
242619ea8026Sopenharmony_ci
242719ea8026Sopenharmony_ci        // update internally tracked dirs
242819ea8026Sopenharmony_ci        for (struct lfs_mlist *d = lfs->mlist; d; d = d->next) {
242919ea8026Sopenharmony_ci            if (lfs_pair_cmp(lpair, d->m.pair) == 0) {
243019ea8026Sopenharmony_ci                d->m.pair[0] = ldir.pair[0];
243119ea8026Sopenharmony_ci                d->m.pair[1] = ldir.pair[1];
243219ea8026Sopenharmony_ci            }
243319ea8026Sopenharmony_ci
243419ea8026Sopenharmony_ci            if (d->type == LFS_TYPE_DIR &&
243519ea8026Sopenharmony_ci                    lfs_pair_cmp(lpair, ((lfs_dir_t*)d)->head) == 0) {
243619ea8026Sopenharmony_ci                ((lfs_dir_t*)d)->head[0] = ldir.pair[0];
243719ea8026Sopenharmony_ci                ((lfs_dir_t*)d)->head[1] = ldir.pair[1];
243819ea8026Sopenharmony_ci            }
243919ea8026Sopenharmony_ci        }
244019ea8026Sopenharmony_ci
244119ea8026Sopenharmony_ci        // find parent
244219ea8026Sopenharmony_ci        lfs_stag_t tag = lfs_fs_parent(lfs, lpair, &pdir);
244319ea8026Sopenharmony_ci        if (tag < 0 && tag != LFS_ERR_NOENT) {
244419ea8026Sopenharmony_ci            return tag;
244519ea8026Sopenharmony_ci        }
244619ea8026Sopenharmony_ci
244719ea8026Sopenharmony_ci        bool hasparent = (tag != LFS_ERR_NOENT);
244819ea8026Sopenharmony_ci        if (tag != LFS_ERR_NOENT) {
244919ea8026Sopenharmony_ci            // note that if we have a parent, we must have a pred, so this will
245019ea8026Sopenharmony_ci            // always create an orphan
245119ea8026Sopenharmony_ci            int err = lfs_fs_preporphans(lfs, +1);
245219ea8026Sopenharmony_ci            if (err) {
245319ea8026Sopenharmony_ci                return err;
245419ea8026Sopenharmony_ci            }
245519ea8026Sopenharmony_ci
245619ea8026Sopenharmony_ci            // fix pending move in this pair? this looks like an optimization but
245719ea8026Sopenharmony_ci            // is in fact _required_ since relocating may outdate the move.
245819ea8026Sopenharmony_ci            uint16_t moveid = 0x3ff;
245919ea8026Sopenharmony_ci            if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) {
246019ea8026Sopenharmony_ci                moveid = lfs_tag_id(lfs->gstate.tag);
246119ea8026Sopenharmony_ci                LFS_DEBUG("Fixing move while relocating "
246219ea8026Sopenharmony_ci                        "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n",
246319ea8026Sopenharmony_ci                        pdir.pair[0], pdir.pair[1], moveid);
246419ea8026Sopenharmony_ci                lfs_fs_prepmove(lfs, 0x3ff, NULL);
246519ea8026Sopenharmony_ci                if (moveid < lfs_tag_id(tag)) {
246619ea8026Sopenharmony_ci                    tag -= LFS_MKTAG(0, 1, 0);
246719ea8026Sopenharmony_ci                }
246819ea8026Sopenharmony_ci            }
246919ea8026Sopenharmony_ci
247019ea8026Sopenharmony_ci            lfs_block_t ppair[2] = {pdir.pair[0], pdir.pair[1]};
247119ea8026Sopenharmony_ci            lfs_pair_tole32(ldir.pair);
247219ea8026Sopenharmony_ci            state = lfs_dir_relocatingcommit(lfs, &pdir, ppair, LFS_MKATTRS(
247319ea8026Sopenharmony_ci                        {LFS_MKTAG_IF(moveid != 0x3ff,
247419ea8026Sopenharmony_ci                            LFS_TYPE_DELETE, moveid, 0), NULL},
247519ea8026Sopenharmony_ci                        {tag, ldir.pair}),
247619ea8026Sopenharmony_ci                    NULL);
247719ea8026Sopenharmony_ci            lfs_pair_fromle32(ldir.pair);
247819ea8026Sopenharmony_ci            if (state < 0) {
247919ea8026Sopenharmony_ci                return state;
248019ea8026Sopenharmony_ci            }
248119ea8026Sopenharmony_ci
248219ea8026Sopenharmony_ci            if (state == LFS_OK_RELOCATED) {
248319ea8026Sopenharmony_ci                lpair[0] = ppair[0];
248419ea8026Sopenharmony_ci                lpair[1] = ppair[1];
248519ea8026Sopenharmony_ci                ldir = pdir;
248619ea8026Sopenharmony_ci                orphans = true;
248719ea8026Sopenharmony_ci                continue;
248819ea8026Sopenharmony_ci            }
248919ea8026Sopenharmony_ci        }
249019ea8026Sopenharmony_ci
249119ea8026Sopenharmony_ci        // find pred
249219ea8026Sopenharmony_ci        int err = lfs_fs_pred(lfs, lpair, &pdir);
249319ea8026Sopenharmony_ci        if (err && err != LFS_ERR_NOENT) {
249419ea8026Sopenharmony_ci            return err;
249519ea8026Sopenharmony_ci        }
249619ea8026Sopenharmony_ci        LFS_ASSERT(!(hasparent && err == LFS_ERR_NOENT));
249719ea8026Sopenharmony_ci
249819ea8026Sopenharmony_ci        // if we can't find dir, it must be new
249919ea8026Sopenharmony_ci        if (err != LFS_ERR_NOENT) {
250019ea8026Sopenharmony_ci            if (lfs_gstate_hasorphans(&lfs->gstate)) {
250119ea8026Sopenharmony_ci                // next step, clean up orphans
250219ea8026Sopenharmony_ci                err = lfs_fs_preporphans(lfs, -hasparent);
250319ea8026Sopenharmony_ci                if (err) {
250419ea8026Sopenharmony_ci                    return err;
250519ea8026Sopenharmony_ci                }
250619ea8026Sopenharmony_ci            }
250719ea8026Sopenharmony_ci
250819ea8026Sopenharmony_ci            // fix pending move in this pair? this looks like an optimization
250919ea8026Sopenharmony_ci            // but is in fact _required_ since relocating may outdate the move.
251019ea8026Sopenharmony_ci            uint16_t moveid = 0x3ff;
251119ea8026Sopenharmony_ci            if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) {
251219ea8026Sopenharmony_ci                moveid = lfs_tag_id(lfs->gstate.tag);
251319ea8026Sopenharmony_ci                LFS_DEBUG("Fixing move while relocating "
251419ea8026Sopenharmony_ci                        "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n",
251519ea8026Sopenharmony_ci                        pdir.pair[0], pdir.pair[1], moveid);
251619ea8026Sopenharmony_ci                lfs_fs_prepmove(lfs, 0x3ff, NULL);
251719ea8026Sopenharmony_ci            }
251819ea8026Sopenharmony_ci
251919ea8026Sopenharmony_ci            // replace bad pair, either we clean up desync, or no desync occured
252019ea8026Sopenharmony_ci            lpair[0] = pdir.pair[0];
252119ea8026Sopenharmony_ci            lpair[1] = pdir.pair[1];
252219ea8026Sopenharmony_ci            lfs_pair_tole32(ldir.pair);
252319ea8026Sopenharmony_ci            state = lfs_dir_relocatingcommit(lfs, &pdir, lpair, LFS_MKATTRS(
252419ea8026Sopenharmony_ci                        {LFS_MKTAG_IF(moveid != 0x3ff,
252519ea8026Sopenharmony_ci                            LFS_TYPE_DELETE, moveid, 0), NULL},
252619ea8026Sopenharmony_ci                        {LFS_MKTAG(LFS_TYPE_TAIL + pdir.split, 0x3ff, 8),
252719ea8026Sopenharmony_ci                            ldir.pair}),
252819ea8026Sopenharmony_ci                    NULL);
252919ea8026Sopenharmony_ci            lfs_pair_fromle32(ldir.pair);
253019ea8026Sopenharmony_ci            if (state < 0) {
253119ea8026Sopenharmony_ci                return state;
253219ea8026Sopenharmony_ci            }
253319ea8026Sopenharmony_ci
253419ea8026Sopenharmony_ci            ldir = pdir;
253519ea8026Sopenharmony_ci        }
253619ea8026Sopenharmony_ci    }
253719ea8026Sopenharmony_ci
253819ea8026Sopenharmony_ci    return orphans ? LFS_OK_ORPHANED : 0;
253919ea8026Sopenharmony_ci}
254019ea8026Sopenharmony_ci#endif
254119ea8026Sopenharmony_ci
254219ea8026Sopenharmony_ci#ifndef LFS_READONLY
254319ea8026Sopenharmony_cistatic int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir,
254419ea8026Sopenharmony_ci        const struct lfs_mattr *attrs, int attrcount) {
254519ea8026Sopenharmony_ci    int orphans = lfs_dir_orphaningcommit(lfs, dir, attrs, attrcount);
254619ea8026Sopenharmony_ci    if (orphans < 0) {
254719ea8026Sopenharmony_ci        return orphans;
254819ea8026Sopenharmony_ci    }
254919ea8026Sopenharmony_ci
255019ea8026Sopenharmony_ci    if (orphans) {
255119ea8026Sopenharmony_ci        // make sure we've removed all orphans, this is a noop if there
255219ea8026Sopenharmony_ci        // are none, but if we had nested blocks failures we may have
255319ea8026Sopenharmony_ci        // created some
255419ea8026Sopenharmony_ci        int err = lfs_fs_deorphan(lfs, false);
255519ea8026Sopenharmony_ci        if (err) {
255619ea8026Sopenharmony_ci            return err;
255719ea8026Sopenharmony_ci        }
255819ea8026Sopenharmony_ci    }
255919ea8026Sopenharmony_ci
256019ea8026Sopenharmony_ci    return 0;
256119ea8026Sopenharmony_ci}
256219ea8026Sopenharmony_ci#endif
256319ea8026Sopenharmony_ci
256419ea8026Sopenharmony_ci
256519ea8026Sopenharmony_ci/// Top level directory operations ///
256619ea8026Sopenharmony_ci#ifndef LFS_READONLY
256719ea8026Sopenharmony_cistatic int lfs_rawmkdir(lfs_t *lfs, const char *path) {
256819ea8026Sopenharmony_ci    // deorphan if we haven't yet, needed at most once after poweron
256919ea8026Sopenharmony_ci    int err = lfs_fs_forceconsistency(lfs);
257019ea8026Sopenharmony_ci    if (err) {
257119ea8026Sopenharmony_ci        return err;
257219ea8026Sopenharmony_ci    }
257319ea8026Sopenharmony_ci
257419ea8026Sopenharmony_ci    struct lfs_mlist cwd;
257519ea8026Sopenharmony_ci    cwd.next = lfs->mlist;
257619ea8026Sopenharmony_ci    uint16_t id;
257719ea8026Sopenharmony_ci    err = lfs_dir_find(lfs, &cwd.m, &path, &id);
257819ea8026Sopenharmony_ci    if (!(err == LFS_ERR_NOENT && id != 0x3ff)) {
257919ea8026Sopenharmony_ci        return (err < 0) ? err : LFS_ERR_EXIST;
258019ea8026Sopenharmony_ci    }
258119ea8026Sopenharmony_ci
258219ea8026Sopenharmony_ci    // check that name fits
258319ea8026Sopenharmony_ci    lfs_size_t nlen = strlen(path);
258419ea8026Sopenharmony_ci    if (nlen > lfs->name_max) {
258519ea8026Sopenharmony_ci        return LFS_ERR_NAMETOOLONG;
258619ea8026Sopenharmony_ci    }
258719ea8026Sopenharmony_ci
258819ea8026Sopenharmony_ci    // build up new directory
258919ea8026Sopenharmony_ci    lfs_alloc_ack(lfs);
259019ea8026Sopenharmony_ci    lfs_mdir_t dir;
259119ea8026Sopenharmony_ci    err = lfs_dir_alloc(lfs, &dir);
259219ea8026Sopenharmony_ci    if (err) {
259319ea8026Sopenharmony_ci        return err;
259419ea8026Sopenharmony_ci    }
259519ea8026Sopenharmony_ci
259619ea8026Sopenharmony_ci    // find end of list
259719ea8026Sopenharmony_ci    lfs_mdir_t pred = cwd.m;
259819ea8026Sopenharmony_ci    while (pred.split) {
259919ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &pred, pred.tail);
260019ea8026Sopenharmony_ci        if (err) {
260119ea8026Sopenharmony_ci            return err;
260219ea8026Sopenharmony_ci        }
260319ea8026Sopenharmony_ci    }
260419ea8026Sopenharmony_ci
260519ea8026Sopenharmony_ci    // setup dir
260619ea8026Sopenharmony_ci    lfs_pair_tole32(pred.tail);
260719ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &dir, LFS_MKATTRS(
260819ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail}));
260919ea8026Sopenharmony_ci    lfs_pair_fromle32(pred.tail);
261019ea8026Sopenharmony_ci    if (err) {
261119ea8026Sopenharmony_ci        return err;
261219ea8026Sopenharmony_ci    }
261319ea8026Sopenharmony_ci
261419ea8026Sopenharmony_ci    // current block not end of list?
261519ea8026Sopenharmony_ci    if (cwd.m.split) {
261619ea8026Sopenharmony_ci        // update tails, this creates a desync
261719ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, +1);
261819ea8026Sopenharmony_ci        if (err) {
261919ea8026Sopenharmony_ci            return err;
262019ea8026Sopenharmony_ci        }
262119ea8026Sopenharmony_ci
262219ea8026Sopenharmony_ci        // it's possible our predecessor has to be relocated, and if
262319ea8026Sopenharmony_ci        // our parent is our predecessor's predecessor, this could have
262419ea8026Sopenharmony_ci        // caused our parent to go out of date, fortunately we can hook
262519ea8026Sopenharmony_ci        // ourselves into littlefs to catch this
262619ea8026Sopenharmony_ci        cwd.type = 0;
262719ea8026Sopenharmony_ci        cwd.id = 0;
262819ea8026Sopenharmony_ci        lfs->mlist = &cwd;
262919ea8026Sopenharmony_ci
263019ea8026Sopenharmony_ci        lfs_pair_tole32(dir.pair);
263119ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &pred, LFS_MKATTRS(
263219ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair}));
263319ea8026Sopenharmony_ci        lfs_pair_fromle32(dir.pair);
263419ea8026Sopenharmony_ci        if (err) {
263519ea8026Sopenharmony_ci            lfs->mlist = cwd.next;
263619ea8026Sopenharmony_ci            return err;
263719ea8026Sopenharmony_ci        }
263819ea8026Sopenharmony_ci
263919ea8026Sopenharmony_ci        lfs->mlist = cwd.next;
264019ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, -1);
264119ea8026Sopenharmony_ci        if (err) {
264219ea8026Sopenharmony_ci            return err;
264319ea8026Sopenharmony_ci        }
264419ea8026Sopenharmony_ci    }
264519ea8026Sopenharmony_ci
264619ea8026Sopenharmony_ci    // now insert into our parent block
264719ea8026Sopenharmony_ci    lfs_pair_tole32(dir.pair);
264819ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &cwd.m, LFS_MKATTRS(
264919ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL},
265019ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_DIR, id, nlen), path},
265119ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_DIRSTRUCT, id, 8), dir.pair},
265219ea8026Sopenharmony_ci            {LFS_MKTAG_IF(!cwd.m.split,
265319ea8026Sopenharmony_ci                LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair}));
265419ea8026Sopenharmony_ci    lfs_pair_fromle32(dir.pair);
265519ea8026Sopenharmony_ci    if (err) {
265619ea8026Sopenharmony_ci        return err;
265719ea8026Sopenharmony_ci    }
265819ea8026Sopenharmony_ci
265919ea8026Sopenharmony_ci    return 0;
266019ea8026Sopenharmony_ci}
266119ea8026Sopenharmony_ci#endif
266219ea8026Sopenharmony_ci
266319ea8026Sopenharmony_cistatic int lfs_dir_rawopen(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
266419ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &dir->m, &path, NULL);
266519ea8026Sopenharmony_ci    if (tag < 0) {
266619ea8026Sopenharmony_ci        return tag;
266719ea8026Sopenharmony_ci    }
266819ea8026Sopenharmony_ci
266919ea8026Sopenharmony_ci    if (lfs_tag_type3(tag) != LFS_TYPE_DIR) {
267019ea8026Sopenharmony_ci        return LFS_ERR_NOTDIR;
267119ea8026Sopenharmony_ci    }
267219ea8026Sopenharmony_ci
267319ea8026Sopenharmony_ci    lfs_block_t pair[2];
267419ea8026Sopenharmony_ci    if (lfs_tag_id(tag) == 0x3ff) {
267519ea8026Sopenharmony_ci        // handle root dir separately
267619ea8026Sopenharmony_ci        pair[0] = lfs->root[0];
267719ea8026Sopenharmony_ci        pair[1] = lfs->root[1];
267819ea8026Sopenharmony_ci    } else {
267919ea8026Sopenharmony_ci        // get dir pair from parent
268019ea8026Sopenharmony_ci        lfs_stag_t res = lfs_dir_get(lfs, &dir->m, LFS_MKTAG(0x700, 0x3ff, 0),
268119ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
268219ea8026Sopenharmony_ci        if (res < 0) {
268319ea8026Sopenharmony_ci            return res;
268419ea8026Sopenharmony_ci        }
268519ea8026Sopenharmony_ci        lfs_pair_fromle32(pair);
268619ea8026Sopenharmony_ci    }
268719ea8026Sopenharmony_ci
268819ea8026Sopenharmony_ci    // fetch first pair
268919ea8026Sopenharmony_ci    int err = lfs_dir_fetch(lfs, &dir->m, pair);
269019ea8026Sopenharmony_ci    if (err) {
269119ea8026Sopenharmony_ci        return err;
269219ea8026Sopenharmony_ci    }
269319ea8026Sopenharmony_ci
269419ea8026Sopenharmony_ci    // setup entry
269519ea8026Sopenharmony_ci    dir->head[0] = dir->m.pair[0];
269619ea8026Sopenharmony_ci    dir->head[1] = dir->m.pair[1];
269719ea8026Sopenharmony_ci    dir->id = 0;
269819ea8026Sopenharmony_ci    dir->pos = 0;
269919ea8026Sopenharmony_ci
270019ea8026Sopenharmony_ci    // add to list of mdirs
270119ea8026Sopenharmony_ci    dir->type = LFS_TYPE_DIR;
270219ea8026Sopenharmony_ci    lfs_mlist_append(lfs, (struct lfs_mlist *)dir);
270319ea8026Sopenharmony_ci
270419ea8026Sopenharmony_ci    return 0;
270519ea8026Sopenharmony_ci}
270619ea8026Sopenharmony_ci
270719ea8026Sopenharmony_cistatic int lfs_dir_rawclose(lfs_t *lfs, lfs_dir_t *dir) {
270819ea8026Sopenharmony_ci    // remove from list of mdirs
270919ea8026Sopenharmony_ci    lfs_mlist_remove(lfs, (struct lfs_mlist *)dir);
271019ea8026Sopenharmony_ci
271119ea8026Sopenharmony_ci    return 0;
271219ea8026Sopenharmony_ci}
271319ea8026Sopenharmony_ci
271419ea8026Sopenharmony_cistatic int lfs_dir_rawread(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
271519ea8026Sopenharmony_ci    memset(info, 0, sizeof(*info));
271619ea8026Sopenharmony_ci
271719ea8026Sopenharmony_ci    // special offset for '.' and '..'
271819ea8026Sopenharmony_ci    if (dir->pos == 0) {
271919ea8026Sopenharmony_ci        info->type = LFS_TYPE_DIR;
272019ea8026Sopenharmony_ci        strcpy(info->name, ".");
272119ea8026Sopenharmony_ci        dir->pos += 1;
272219ea8026Sopenharmony_ci        return true;
272319ea8026Sopenharmony_ci    } else if (dir->pos == 1) {
272419ea8026Sopenharmony_ci        info->type = LFS_TYPE_DIR;
272519ea8026Sopenharmony_ci        strcpy(info->name, "..");
272619ea8026Sopenharmony_ci        dir->pos += 1;
272719ea8026Sopenharmony_ci        return true;
272819ea8026Sopenharmony_ci    }
272919ea8026Sopenharmony_ci
273019ea8026Sopenharmony_ci    while (true) {
273119ea8026Sopenharmony_ci        if (dir->id == dir->m.count) {
273219ea8026Sopenharmony_ci            if (!dir->m.split) {
273319ea8026Sopenharmony_ci                return false;
273419ea8026Sopenharmony_ci            }
273519ea8026Sopenharmony_ci
273619ea8026Sopenharmony_ci            int err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail);
273719ea8026Sopenharmony_ci            if (err) {
273819ea8026Sopenharmony_ci                return err;
273919ea8026Sopenharmony_ci            }
274019ea8026Sopenharmony_ci
274119ea8026Sopenharmony_ci            dir->id = 0;
274219ea8026Sopenharmony_ci        }
274319ea8026Sopenharmony_ci
274419ea8026Sopenharmony_ci        int err = lfs_dir_getinfo(lfs, &dir->m, dir->id, info);
274519ea8026Sopenharmony_ci        if (err && err != LFS_ERR_NOENT) {
274619ea8026Sopenharmony_ci            return err;
274719ea8026Sopenharmony_ci        }
274819ea8026Sopenharmony_ci
274919ea8026Sopenharmony_ci        dir->id += 1;
275019ea8026Sopenharmony_ci        if (err != LFS_ERR_NOENT) {
275119ea8026Sopenharmony_ci            break;
275219ea8026Sopenharmony_ci        }
275319ea8026Sopenharmony_ci    }
275419ea8026Sopenharmony_ci
275519ea8026Sopenharmony_ci    dir->pos += 1;
275619ea8026Sopenharmony_ci    return true;
275719ea8026Sopenharmony_ci}
275819ea8026Sopenharmony_ci
275919ea8026Sopenharmony_cistatic int lfs_dir_rawseek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
276019ea8026Sopenharmony_ci    // simply walk from head dir
276119ea8026Sopenharmony_ci    int err = lfs_dir_rawrewind(lfs, dir);
276219ea8026Sopenharmony_ci    if (err) {
276319ea8026Sopenharmony_ci        return err;
276419ea8026Sopenharmony_ci    }
276519ea8026Sopenharmony_ci
276619ea8026Sopenharmony_ci    // first two for ./..
276719ea8026Sopenharmony_ci    dir->pos = lfs_min(2, off);
276819ea8026Sopenharmony_ci    off -= dir->pos;
276919ea8026Sopenharmony_ci
277019ea8026Sopenharmony_ci    // skip superblock entry
277119ea8026Sopenharmony_ci    dir->id = (off > 0 && lfs_pair_cmp(dir->head, lfs->root) == 0);
277219ea8026Sopenharmony_ci
277319ea8026Sopenharmony_ci    while (off > 0) {
277419ea8026Sopenharmony_ci        if (dir->id == dir->m.count) {
277519ea8026Sopenharmony_ci            if (!dir->m.split) {
277619ea8026Sopenharmony_ci                return LFS_ERR_INVAL;
277719ea8026Sopenharmony_ci            }
277819ea8026Sopenharmony_ci
277919ea8026Sopenharmony_ci            err = lfs_dir_fetch(lfs, &dir->m, dir->m.tail);
278019ea8026Sopenharmony_ci            if (err) {
278119ea8026Sopenharmony_ci                return err;
278219ea8026Sopenharmony_ci            }
278319ea8026Sopenharmony_ci
278419ea8026Sopenharmony_ci            dir->id = 0;
278519ea8026Sopenharmony_ci        }
278619ea8026Sopenharmony_ci
278719ea8026Sopenharmony_ci        int diff = lfs_min(dir->m.count - dir->id, off);
278819ea8026Sopenharmony_ci        dir->id += diff;
278919ea8026Sopenharmony_ci        dir->pos += diff;
279019ea8026Sopenharmony_ci        off -= diff;
279119ea8026Sopenharmony_ci    }
279219ea8026Sopenharmony_ci
279319ea8026Sopenharmony_ci    return 0;
279419ea8026Sopenharmony_ci}
279519ea8026Sopenharmony_ci
279619ea8026Sopenharmony_cistatic lfs_soff_t lfs_dir_rawtell(lfs_t *lfs, lfs_dir_t *dir) {
279719ea8026Sopenharmony_ci    (void)lfs;
279819ea8026Sopenharmony_ci    return dir->pos;
279919ea8026Sopenharmony_ci}
280019ea8026Sopenharmony_ci
280119ea8026Sopenharmony_cistatic int lfs_dir_rawrewind(lfs_t *lfs, lfs_dir_t *dir) {
280219ea8026Sopenharmony_ci    // reload the head dir
280319ea8026Sopenharmony_ci    int err = lfs_dir_fetch(lfs, &dir->m, dir->head);
280419ea8026Sopenharmony_ci    if (err) {
280519ea8026Sopenharmony_ci        return err;
280619ea8026Sopenharmony_ci    }
280719ea8026Sopenharmony_ci
280819ea8026Sopenharmony_ci    dir->id = 0;
280919ea8026Sopenharmony_ci    dir->pos = 0;
281019ea8026Sopenharmony_ci    return 0;
281119ea8026Sopenharmony_ci}
281219ea8026Sopenharmony_ci
281319ea8026Sopenharmony_ci
281419ea8026Sopenharmony_ci/// File index list operations ///
281519ea8026Sopenharmony_cistatic int lfs_ctz_index(lfs_t *lfs, lfs_off_t *off) {
281619ea8026Sopenharmony_ci    lfs_off_t size = *off;
281719ea8026Sopenharmony_ci    lfs_off_t b = lfs->cfg->block_size - 2*4;
281819ea8026Sopenharmony_ci    lfs_off_t i = size / b;
281919ea8026Sopenharmony_ci    if (i == 0) {
282019ea8026Sopenharmony_ci        return 0;
282119ea8026Sopenharmony_ci    }
282219ea8026Sopenharmony_ci
282319ea8026Sopenharmony_ci    i = (size - 4*(lfs_popc(i-1)+2)) / b;
282419ea8026Sopenharmony_ci    *off = size - b*i - 4*lfs_popc(i);
282519ea8026Sopenharmony_ci    return i;
282619ea8026Sopenharmony_ci}
282719ea8026Sopenharmony_ci
282819ea8026Sopenharmony_cistatic int lfs_ctz_find(lfs_t *lfs,
282919ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache,
283019ea8026Sopenharmony_ci        lfs_block_t head, lfs_size_t size,
283119ea8026Sopenharmony_ci        lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) {
283219ea8026Sopenharmony_ci    if (size == 0) {
283319ea8026Sopenharmony_ci        *block = LFS_BLOCK_NULL;
283419ea8026Sopenharmony_ci        *off = 0;
283519ea8026Sopenharmony_ci        return 0;
283619ea8026Sopenharmony_ci    }
283719ea8026Sopenharmony_ci
283819ea8026Sopenharmony_ci    lfs_off_t current = lfs_ctz_index(lfs, &(lfs_off_t){size-1});
283919ea8026Sopenharmony_ci    lfs_off_t target = lfs_ctz_index(lfs, &pos);
284019ea8026Sopenharmony_ci
284119ea8026Sopenharmony_ci    while (current > target) {
284219ea8026Sopenharmony_ci        lfs_size_t skip = lfs_min(
284319ea8026Sopenharmony_ci                lfs_npw2(current-target+1) - 1,
284419ea8026Sopenharmony_ci                lfs_ctz(current));
284519ea8026Sopenharmony_ci
284619ea8026Sopenharmony_ci        int err = lfs_bd_read(lfs,
284719ea8026Sopenharmony_ci                pcache, rcache, sizeof(head),
284819ea8026Sopenharmony_ci                head, 4*skip, &head, sizeof(head));
284919ea8026Sopenharmony_ci        head = lfs_fromle32(head);
285019ea8026Sopenharmony_ci        if (err) {
285119ea8026Sopenharmony_ci            return err;
285219ea8026Sopenharmony_ci        }
285319ea8026Sopenharmony_ci
285419ea8026Sopenharmony_ci        current -= 1 << skip;
285519ea8026Sopenharmony_ci    }
285619ea8026Sopenharmony_ci
285719ea8026Sopenharmony_ci    *block = head;
285819ea8026Sopenharmony_ci    *off = pos;
285919ea8026Sopenharmony_ci    return 0;
286019ea8026Sopenharmony_ci}
286119ea8026Sopenharmony_ci
286219ea8026Sopenharmony_ci#ifndef LFS_READONLY
286319ea8026Sopenharmony_cistatic int lfs_ctz_extend(lfs_t *lfs,
286419ea8026Sopenharmony_ci        lfs_cache_t *pcache, lfs_cache_t *rcache,
286519ea8026Sopenharmony_ci        lfs_block_t head, lfs_size_t size,
286619ea8026Sopenharmony_ci        lfs_block_t *block, lfs_off_t *off) {
286719ea8026Sopenharmony_ci    while (true) {
286819ea8026Sopenharmony_ci        // go ahead and grab a block
286919ea8026Sopenharmony_ci        lfs_block_t nblock;
287019ea8026Sopenharmony_ci        int err = lfs_alloc(lfs, &nblock);
287119ea8026Sopenharmony_ci        if (err) {
287219ea8026Sopenharmony_ci            return err;
287319ea8026Sopenharmony_ci        }
287419ea8026Sopenharmony_ci
287519ea8026Sopenharmony_ci        {
287619ea8026Sopenharmony_ci            err = lfs_bd_erase(lfs, nblock);
287719ea8026Sopenharmony_ci            if (err) {
287819ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
287919ea8026Sopenharmony_ci                    goto relocate;
288019ea8026Sopenharmony_ci                }
288119ea8026Sopenharmony_ci                return err;
288219ea8026Sopenharmony_ci            }
288319ea8026Sopenharmony_ci
288419ea8026Sopenharmony_ci            if (size == 0) {
288519ea8026Sopenharmony_ci                *block = nblock;
288619ea8026Sopenharmony_ci                *off = 0;
288719ea8026Sopenharmony_ci                return 0;
288819ea8026Sopenharmony_ci            }
288919ea8026Sopenharmony_ci
289019ea8026Sopenharmony_ci            lfs_size_t noff = size - 1;
289119ea8026Sopenharmony_ci            lfs_off_t index = lfs_ctz_index(lfs, &noff);
289219ea8026Sopenharmony_ci            noff = noff + 1;
289319ea8026Sopenharmony_ci
289419ea8026Sopenharmony_ci            // just copy out the last block if it is incomplete
289519ea8026Sopenharmony_ci            if (noff != lfs->cfg->block_size) {
289619ea8026Sopenharmony_ci                for (lfs_off_t i = 0; i < noff; i++) {
289719ea8026Sopenharmony_ci                    uint8_t data;
289819ea8026Sopenharmony_ci                    err = lfs_bd_read(lfs,
289919ea8026Sopenharmony_ci                            NULL, rcache, noff-i,
290019ea8026Sopenharmony_ci                            head, i, &data, 1);
290119ea8026Sopenharmony_ci                    if (err) {
290219ea8026Sopenharmony_ci                        return err;
290319ea8026Sopenharmony_ci                    }
290419ea8026Sopenharmony_ci
290519ea8026Sopenharmony_ci                    err = lfs_bd_prog(lfs,
290619ea8026Sopenharmony_ci                            pcache, rcache, true,
290719ea8026Sopenharmony_ci                            nblock, i, &data, 1);
290819ea8026Sopenharmony_ci                    if (err) {
290919ea8026Sopenharmony_ci                        if (err == LFS_ERR_CORRUPT) {
291019ea8026Sopenharmony_ci                            goto relocate;
291119ea8026Sopenharmony_ci                        }
291219ea8026Sopenharmony_ci                        return err;
291319ea8026Sopenharmony_ci                    }
291419ea8026Sopenharmony_ci                }
291519ea8026Sopenharmony_ci
291619ea8026Sopenharmony_ci                *block = nblock;
291719ea8026Sopenharmony_ci                *off = noff;
291819ea8026Sopenharmony_ci                return 0;
291919ea8026Sopenharmony_ci            }
292019ea8026Sopenharmony_ci
292119ea8026Sopenharmony_ci            // append block
292219ea8026Sopenharmony_ci            index += 1;
292319ea8026Sopenharmony_ci            lfs_size_t skips = lfs_ctz(index) + 1;
292419ea8026Sopenharmony_ci            lfs_block_t nhead = head;
292519ea8026Sopenharmony_ci            for (lfs_off_t i = 0; i < skips; i++) {
292619ea8026Sopenharmony_ci                nhead = lfs_tole32(nhead);
292719ea8026Sopenharmony_ci                err = lfs_bd_prog(lfs, pcache, rcache, true,
292819ea8026Sopenharmony_ci                        nblock, 4*i, &nhead, 4);
292919ea8026Sopenharmony_ci                nhead = lfs_fromle32(nhead);
293019ea8026Sopenharmony_ci                if (err) {
293119ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
293219ea8026Sopenharmony_ci                        goto relocate;
293319ea8026Sopenharmony_ci                    }
293419ea8026Sopenharmony_ci                    return err;
293519ea8026Sopenharmony_ci                }
293619ea8026Sopenharmony_ci
293719ea8026Sopenharmony_ci                if (i != skips-1) {
293819ea8026Sopenharmony_ci                    err = lfs_bd_read(lfs,
293919ea8026Sopenharmony_ci                            NULL, rcache, sizeof(nhead),
294019ea8026Sopenharmony_ci                            nhead, 4*i, &nhead, sizeof(nhead));
294119ea8026Sopenharmony_ci                    nhead = lfs_fromle32(nhead);
294219ea8026Sopenharmony_ci                    if (err) {
294319ea8026Sopenharmony_ci                        return err;
294419ea8026Sopenharmony_ci                    }
294519ea8026Sopenharmony_ci                }
294619ea8026Sopenharmony_ci            }
294719ea8026Sopenharmony_ci
294819ea8026Sopenharmony_ci            *block = nblock;
294919ea8026Sopenharmony_ci            *off = 4*skips;
295019ea8026Sopenharmony_ci            return 0;
295119ea8026Sopenharmony_ci        }
295219ea8026Sopenharmony_ci
295319ea8026Sopenharmony_cirelocate:
295419ea8026Sopenharmony_ci        LFS_DEBUG("Bad block at 0x%"PRIx32, nblock);
295519ea8026Sopenharmony_ci
295619ea8026Sopenharmony_ci        // just clear cache and try a new block
295719ea8026Sopenharmony_ci        lfs_cache_drop(lfs, pcache);
295819ea8026Sopenharmony_ci    }
295919ea8026Sopenharmony_ci}
296019ea8026Sopenharmony_ci#endif
296119ea8026Sopenharmony_ci
296219ea8026Sopenharmony_cistatic int lfs_ctz_traverse(lfs_t *lfs,
296319ea8026Sopenharmony_ci        const lfs_cache_t *pcache, lfs_cache_t *rcache,
296419ea8026Sopenharmony_ci        lfs_block_t head, lfs_size_t size,
296519ea8026Sopenharmony_ci        int (*cb)(void*, lfs_block_t), void *data) {
296619ea8026Sopenharmony_ci    if (size == 0) {
296719ea8026Sopenharmony_ci        return 0;
296819ea8026Sopenharmony_ci    }
296919ea8026Sopenharmony_ci
297019ea8026Sopenharmony_ci    lfs_off_t index = lfs_ctz_index(lfs, &(lfs_off_t){size-1});
297119ea8026Sopenharmony_ci
297219ea8026Sopenharmony_ci    while (true) {
297319ea8026Sopenharmony_ci        int err = cb(data, head);
297419ea8026Sopenharmony_ci        if (err) {
297519ea8026Sopenharmony_ci            return err;
297619ea8026Sopenharmony_ci        }
297719ea8026Sopenharmony_ci
297819ea8026Sopenharmony_ci        if (index == 0) {
297919ea8026Sopenharmony_ci            return 0;
298019ea8026Sopenharmony_ci        }
298119ea8026Sopenharmony_ci
298219ea8026Sopenharmony_ci        lfs_block_t heads[2];
298319ea8026Sopenharmony_ci        int count = 2 - (index & 1);
298419ea8026Sopenharmony_ci        err = lfs_bd_read(lfs,
298519ea8026Sopenharmony_ci                pcache, rcache, count*sizeof(head),
298619ea8026Sopenharmony_ci                head, 0, &heads, count*sizeof(head));
298719ea8026Sopenharmony_ci        heads[0] = lfs_fromle32(heads[0]);
298819ea8026Sopenharmony_ci        heads[1] = lfs_fromle32(heads[1]);
298919ea8026Sopenharmony_ci        if (err) {
299019ea8026Sopenharmony_ci            return err;
299119ea8026Sopenharmony_ci        }
299219ea8026Sopenharmony_ci
299319ea8026Sopenharmony_ci        for (int i = 0; i < count-1; i++) {
299419ea8026Sopenharmony_ci            err = cb(data, heads[i]);
299519ea8026Sopenharmony_ci            if (err) {
299619ea8026Sopenharmony_ci                return err;
299719ea8026Sopenharmony_ci            }
299819ea8026Sopenharmony_ci        }
299919ea8026Sopenharmony_ci
300019ea8026Sopenharmony_ci        head = heads[count-1];
300119ea8026Sopenharmony_ci        index -= count;
300219ea8026Sopenharmony_ci    }
300319ea8026Sopenharmony_ci}
300419ea8026Sopenharmony_ci
300519ea8026Sopenharmony_ci
300619ea8026Sopenharmony_ci/// Top level file operations ///
300719ea8026Sopenharmony_cistatic int lfs_file_rawopencfg(lfs_t *lfs, lfs_file_t *file,
300819ea8026Sopenharmony_ci        const char *path, int flags,
300919ea8026Sopenharmony_ci        const struct lfs_file_config *cfg) {
301019ea8026Sopenharmony_ci#ifndef LFS_READONLY
301119ea8026Sopenharmony_ci    // deorphan if we haven't yet, needed at most once after poweron
301219ea8026Sopenharmony_ci    if ((flags & LFS_O_WRONLY) == LFS_O_WRONLY) {
301319ea8026Sopenharmony_ci        int err = lfs_fs_forceconsistency(lfs);
301419ea8026Sopenharmony_ci        if (err) {
301519ea8026Sopenharmony_ci            return err;
301619ea8026Sopenharmony_ci        }
301719ea8026Sopenharmony_ci    }
301819ea8026Sopenharmony_ci#else
301919ea8026Sopenharmony_ci    LFS_ASSERT((flags & LFS_O_RDONLY) == LFS_O_RDONLY);
302019ea8026Sopenharmony_ci#endif
302119ea8026Sopenharmony_ci
302219ea8026Sopenharmony_ci    // setup simple file details
302319ea8026Sopenharmony_ci    int err;
302419ea8026Sopenharmony_ci    file->cfg = cfg;
302519ea8026Sopenharmony_ci    file->flags = flags;
302619ea8026Sopenharmony_ci    file->pos = 0;
302719ea8026Sopenharmony_ci    file->off = 0;
302819ea8026Sopenharmony_ci    file->cache.buffer = NULL;
302919ea8026Sopenharmony_ci
303019ea8026Sopenharmony_ci    // allocate entry for file if it doesn't exist
303119ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &file->m, &path, &file->id);
303219ea8026Sopenharmony_ci    if (tag < 0 && !(tag == LFS_ERR_NOENT && file->id != 0x3ff)) {
303319ea8026Sopenharmony_ci        err = tag;
303419ea8026Sopenharmony_ci        goto cleanup;
303519ea8026Sopenharmony_ci    }
303619ea8026Sopenharmony_ci
303719ea8026Sopenharmony_ci    // get id, add to list of mdirs to catch update changes
303819ea8026Sopenharmony_ci    file->type = LFS_TYPE_REG;
303919ea8026Sopenharmony_ci    lfs_mlist_append(lfs, (struct lfs_mlist *)file);
304019ea8026Sopenharmony_ci
304119ea8026Sopenharmony_ci#ifdef LFS_READONLY
304219ea8026Sopenharmony_ci    if (tag == LFS_ERR_NOENT) {
304319ea8026Sopenharmony_ci        err = LFS_ERR_NOENT;
304419ea8026Sopenharmony_ci        goto cleanup;
304519ea8026Sopenharmony_ci#else
304619ea8026Sopenharmony_ci    if (tag == LFS_ERR_NOENT) {
304719ea8026Sopenharmony_ci        if (!(flags & LFS_O_CREAT)) {
304819ea8026Sopenharmony_ci            err = LFS_ERR_NOENT;
304919ea8026Sopenharmony_ci            goto cleanup;
305019ea8026Sopenharmony_ci        }
305119ea8026Sopenharmony_ci
305219ea8026Sopenharmony_ci        // check that name fits
305319ea8026Sopenharmony_ci        lfs_size_t nlen = strlen(path);
305419ea8026Sopenharmony_ci        if (nlen > lfs->name_max) {
305519ea8026Sopenharmony_ci            err = LFS_ERR_NAMETOOLONG;
305619ea8026Sopenharmony_ci            goto cleanup;
305719ea8026Sopenharmony_ci        }
305819ea8026Sopenharmony_ci
305919ea8026Sopenharmony_ci        // get next slot and create entry to remember name
306019ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
306119ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_CREATE, file->id, 0), NULL},
306219ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_REG, file->id, nlen), path},
306319ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0), NULL}));
306419ea8026Sopenharmony_ci
306519ea8026Sopenharmony_ci        // it may happen that the file name doesn't fit in the metadata blocks, e.g., a 256 byte file name will
306619ea8026Sopenharmony_ci        // not fit in a 128 byte block.
306719ea8026Sopenharmony_ci        err = (err == LFS_ERR_NOSPC) ? LFS_ERR_NAMETOOLONG : err;
306819ea8026Sopenharmony_ci        if (err) {
306919ea8026Sopenharmony_ci            goto cleanup;
307019ea8026Sopenharmony_ci        }
307119ea8026Sopenharmony_ci
307219ea8026Sopenharmony_ci        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, 0);
307319ea8026Sopenharmony_ci    } else if (flags & LFS_O_EXCL) {
307419ea8026Sopenharmony_ci        err = LFS_ERR_EXIST;
307519ea8026Sopenharmony_ci        goto cleanup;
307619ea8026Sopenharmony_ci#endif
307719ea8026Sopenharmony_ci    } else if (lfs_tag_type3(tag) != LFS_TYPE_REG) {
307819ea8026Sopenharmony_ci        err = LFS_ERR_ISDIR;
307919ea8026Sopenharmony_ci        goto cleanup;
308019ea8026Sopenharmony_ci#ifndef LFS_READONLY
308119ea8026Sopenharmony_ci    } else if (flags & LFS_O_TRUNC) {
308219ea8026Sopenharmony_ci        // truncate if requested
308319ea8026Sopenharmony_ci        tag = LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0);
308419ea8026Sopenharmony_ci        file->flags |= LFS_F_DIRTY;
308519ea8026Sopenharmony_ci#endif
308619ea8026Sopenharmony_ci    } else {
308719ea8026Sopenharmony_ci        // try to load what's on disk, if it's inlined we'll fix it later
308819ea8026Sopenharmony_ci        tag = lfs_dir_get(lfs, &file->m, LFS_MKTAG(0x700, 0x3ff, 0),
308919ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_STRUCT, file->id, 8), &file->ctz);
309019ea8026Sopenharmony_ci        if (tag < 0) {
309119ea8026Sopenharmony_ci            err = tag;
309219ea8026Sopenharmony_ci            goto cleanup;
309319ea8026Sopenharmony_ci        }
309419ea8026Sopenharmony_ci        lfs_ctz_fromle32(&file->ctz);
309519ea8026Sopenharmony_ci    }
309619ea8026Sopenharmony_ci
309719ea8026Sopenharmony_ci    // fetch attrs
309819ea8026Sopenharmony_ci    for (unsigned i = 0; i < file->cfg->attr_count; i++) {
309919ea8026Sopenharmony_ci        // if opened for read / read-write operations
310019ea8026Sopenharmony_ci        if ((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY) {
310119ea8026Sopenharmony_ci            lfs_stag_t res = lfs_dir_get(lfs, &file->m,
310219ea8026Sopenharmony_ci                    LFS_MKTAG(0x7ff, 0x3ff, 0),
310319ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_USERATTR + file->cfg->attrs[i].type,
310419ea8026Sopenharmony_ci                        file->id, file->cfg->attrs[i].size),
310519ea8026Sopenharmony_ci                        file->cfg->attrs[i].buffer);
310619ea8026Sopenharmony_ci            if (res < 0 && res != LFS_ERR_NOENT) {
310719ea8026Sopenharmony_ci                err = res;
310819ea8026Sopenharmony_ci                goto cleanup;
310919ea8026Sopenharmony_ci            }
311019ea8026Sopenharmony_ci        }
311119ea8026Sopenharmony_ci
311219ea8026Sopenharmony_ci#ifndef LFS_READONLY
311319ea8026Sopenharmony_ci        // if opened for write / read-write operations
311419ea8026Sopenharmony_ci        if ((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY) {
311519ea8026Sopenharmony_ci            if (file->cfg->attrs[i].size > lfs->attr_max) {
311619ea8026Sopenharmony_ci                err = LFS_ERR_NOSPC;
311719ea8026Sopenharmony_ci                goto cleanup;
311819ea8026Sopenharmony_ci            }
311919ea8026Sopenharmony_ci
312019ea8026Sopenharmony_ci            file->flags |= LFS_F_DIRTY;
312119ea8026Sopenharmony_ci        }
312219ea8026Sopenharmony_ci#endif
312319ea8026Sopenharmony_ci    }
312419ea8026Sopenharmony_ci
312519ea8026Sopenharmony_ci    // allocate buffer if needed
312619ea8026Sopenharmony_ci    if (file->cfg->buffer) {
312719ea8026Sopenharmony_ci        file->cache.buffer = file->cfg->buffer;
312819ea8026Sopenharmony_ci    } else {
312919ea8026Sopenharmony_ci        file->cache.buffer = lfs_malloc(lfs->cfg->cache_size);
313019ea8026Sopenharmony_ci        if (!file->cache.buffer) {
313119ea8026Sopenharmony_ci            err = LFS_ERR_NOMEM;
313219ea8026Sopenharmony_ci            goto cleanup;
313319ea8026Sopenharmony_ci        }
313419ea8026Sopenharmony_ci    }
313519ea8026Sopenharmony_ci
313619ea8026Sopenharmony_ci    // zero to avoid information leak
313719ea8026Sopenharmony_ci    lfs_cache_zero(lfs, &file->cache);
313819ea8026Sopenharmony_ci
313919ea8026Sopenharmony_ci    if (lfs_tag_type3(tag) == LFS_TYPE_INLINESTRUCT) {
314019ea8026Sopenharmony_ci        // load inline files
314119ea8026Sopenharmony_ci        file->ctz.head = LFS_BLOCK_INLINE;
314219ea8026Sopenharmony_ci        file->ctz.size = lfs_tag_size(tag);
314319ea8026Sopenharmony_ci        file->flags |= LFS_F_INLINE;
314419ea8026Sopenharmony_ci        file->cache.block = file->ctz.head;
314519ea8026Sopenharmony_ci        file->cache.off = 0;
314619ea8026Sopenharmony_ci        file->cache.size = lfs->cfg->cache_size;
314719ea8026Sopenharmony_ci
314819ea8026Sopenharmony_ci        // don't always read (may be new/trunc file)
314919ea8026Sopenharmony_ci        if (file->ctz.size > 0) {
315019ea8026Sopenharmony_ci            lfs_stag_t res = lfs_dir_get(lfs, &file->m,
315119ea8026Sopenharmony_ci                    LFS_MKTAG(0x700, 0x3ff, 0),
315219ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_STRUCT, file->id,
315319ea8026Sopenharmony_ci                        lfs_min(file->cache.size, 0x3fe)),
315419ea8026Sopenharmony_ci                    file->cache.buffer);
315519ea8026Sopenharmony_ci            if (res < 0) {
315619ea8026Sopenharmony_ci                err = res;
315719ea8026Sopenharmony_ci                goto cleanup;
315819ea8026Sopenharmony_ci            }
315919ea8026Sopenharmony_ci        }
316019ea8026Sopenharmony_ci    }
316119ea8026Sopenharmony_ci
316219ea8026Sopenharmony_ci    return 0;
316319ea8026Sopenharmony_ci
316419ea8026Sopenharmony_cicleanup:
316519ea8026Sopenharmony_ci    // clean up lingering resources
316619ea8026Sopenharmony_ci#ifndef LFS_READONLY
316719ea8026Sopenharmony_ci    file->flags |= LFS_F_ERRED;
316819ea8026Sopenharmony_ci#endif
316919ea8026Sopenharmony_ci    lfs_file_rawclose(lfs, file);
317019ea8026Sopenharmony_ci    return err;
317119ea8026Sopenharmony_ci}
317219ea8026Sopenharmony_ci
317319ea8026Sopenharmony_ci#ifndef LFS_NO_MALLOC
317419ea8026Sopenharmony_cistatic int lfs_file_rawopen(lfs_t *lfs, lfs_file_t *file,
317519ea8026Sopenharmony_ci        const char *path, int flags) {
317619ea8026Sopenharmony_ci    static const struct lfs_file_config defaults = {0};
317719ea8026Sopenharmony_ci    int err = lfs_file_rawopencfg(lfs, file, path, flags, &defaults);
317819ea8026Sopenharmony_ci    return err;
317919ea8026Sopenharmony_ci}
318019ea8026Sopenharmony_ci#endif
318119ea8026Sopenharmony_ci
318219ea8026Sopenharmony_cistatic int lfs_file_rawclose(lfs_t *lfs, lfs_file_t *file) {
318319ea8026Sopenharmony_ci#ifndef LFS_READONLY
318419ea8026Sopenharmony_ci    int err = lfs_file_rawsync(lfs, file);
318519ea8026Sopenharmony_ci#else
318619ea8026Sopenharmony_ci    int err = 0;
318719ea8026Sopenharmony_ci#endif
318819ea8026Sopenharmony_ci
318919ea8026Sopenharmony_ci    // remove from list of mdirs
319019ea8026Sopenharmony_ci    lfs_mlist_remove(lfs, (struct lfs_mlist*)file);
319119ea8026Sopenharmony_ci
319219ea8026Sopenharmony_ci    // clean up memory
319319ea8026Sopenharmony_ci    if (!file->cfg->buffer) {
319419ea8026Sopenharmony_ci        lfs_free(file->cache.buffer);
319519ea8026Sopenharmony_ci    }
319619ea8026Sopenharmony_ci
319719ea8026Sopenharmony_ci    return err;
319819ea8026Sopenharmony_ci}
319919ea8026Sopenharmony_ci
320019ea8026Sopenharmony_ci
320119ea8026Sopenharmony_ci#ifndef LFS_READONLY
320219ea8026Sopenharmony_cistatic int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) {
320319ea8026Sopenharmony_ci    while (true) {
320419ea8026Sopenharmony_ci        // just relocate what exists into new block
320519ea8026Sopenharmony_ci        lfs_block_t nblock;
320619ea8026Sopenharmony_ci        int err = lfs_alloc(lfs, &nblock);
320719ea8026Sopenharmony_ci        if (err) {
320819ea8026Sopenharmony_ci            return err;
320919ea8026Sopenharmony_ci        }
321019ea8026Sopenharmony_ci
321119ea8026Sopenharmony_ci        err = lfs_bd_erase(lfs, nblock);
321219ea8026Sopenharmony_ci        if (err) {
321319ea8026Sopenharmony_ci            if (err == LFS_ERR_CORRUPT) {
321419ea8026Sopenharmony_ci                goto relocate;
321519ea8026Sopenharmony_ci            }
321619ea8026Sopenharmony_ci            return err;
321719ea8026Sopenharmony_ci        }
321819ea8026Sopenharmony_ci
321919ea8026Sopenharmony_ci        // either read from dirty cache or disk
322019ea8026Sopenharmony_ci        for (lfs_off_t i = 0; i < file->off; i++) {
322119ea8026Sopenharmony_ci            uint8_t data;
322219ea8026Sopenharmony_ci            if (file->flags & LFS_F_INLINE) {
322319ea8026Sopenharmony_ci                err = lfs_dir_getread(lfs, &file->m,
322419ea8026Sopenharmony_ci                        // note we evict inline files before they can be dirty
322519ea8026Sopenharmony_ci                        NULL, &file->cache, file->off-i,
322619ea8026Sopenharmony_ci                        LFS_MKTAG(0xfff, 0x1ff, 0),
322719ea8026Sopenharmony_ci                        LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
322819ea8026Sopenharmony_ci                        i, &data, 1);
322919ea8026Sopenharmony_ci                if (err) {
323019ea8026Sopenharmony_ci                    return err;
323119ea8026Sopenharmony_ci                }
323219ea8026Sopenharmony_ci            } else {
323319ea8026Sopenharmony_ci                err = lfs_bd_read(lfs,
323419ea8026Sopenharmony_ci                        &file->cache, &lfs->rcache, file->off-i,
323519ea8026Sopenharmony_ci                        file->block, i, &data, 1);
323619ea8026Sopenharmony_ci                if (err) {
323719ea8026Sopenharmony_ci                    return err;
323819ea8026Sopenharmony_ci                }
323919ea8026Sopenharmony_ci            }
324019ea8026Sopenharmony_ci
324119ea8026Sopenharmony_ci            err = lfs_bd_prog(lfs,
324219ea8026Sopenharmony_ci                    &lfs->pcache, &lfs->rcache, true,
324319ea8026Sopenharmony_ci                    nblock, i, &data, 1);
324419ea8026Sopenharmony_ci            if (err) {
324519ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
324619ea8026Sopenharmony_ci                    goto relocate;
324719ea8026Sopenharmony_ci                }
324819ea8026Sopenharmony_ci                return err;
324919ea8026Sopenharmony_ci            }
325019ea8026Sopenharmony_ci        }
325119ea8026Sopenharmony_ci
325219ea8026Sopenharmony_ci        // copy over new state of file
325319ea8026Sopenharmony_ci        memcpy(file->cache.buffer, lfs->pcache.buffer, lfs->cfg->cache_size);
325419ea8026Sopenharmony_ci        file->cache.block = lfs->pcache.block;
325519ea8026Sopenharmony_ci        file->cache.off = lfs->pcache.off;
325619ea8026Sopenharmony_ci        file->cache.size = lfs->pcache.size;
325719ea8026Sopenharmony_ci        lfs_cache_zero(lfs, &lfs->pcache);
325819ea8026Sopenharmony_ci
325919ea8026Sopenharmony_ci        file->block = nblock;
326019ea8026Sopenharmony_ci        file->flags |= LFS_F_WRITING;
326119ea8026Sopenharmony_ci        return 0;
326219ea8026Sopenharmony_ci
326319ea8026Sopenharmony_cirelocate:
326419ea8026Sopenharmony_ci        LFS_DEBUG("Bad block at 0x%"PRIx32, nblock);
326519ea8026Sopenharmony_ci
326619ea8026Sopenharmony_ci        // just clear cache and try a new block
326719ea8026Sopenharmony_ci        lfs_cache_drop(lfs, &lfs->pcache);
326819ea8026Sopenharmony_ci    }
326919ea8026Sopenharmony_ci}
327019ea8026Sopenharmony_ci#endif
327119ea8026Sopenharmony_ci
327219ea8026Sopenharmony_ci#ifndef LFS_READONLY
327319ea8026Sopenharmony_cistatic int lfs_file_outline(lfs_t *lfs, lfs_file_t *file) {
327419ea8026Sopenharmony_ci    file->off = file->pos;
327519ea8026Sopenharmony_ci    lfs_alloc_ack(lfs);
327619ea8026Sopenharmony_ci    int err = lfs_file_relocate(lfs, file);
327719ea8026Sopenharmony_ci    if (err) {
327819ea8026Sopenharmony_ci        return err;
327919ea8026Sopenharmony_ci    }
328019ea8026Sopenharmony_ci
328119ea8026Sopenharmony_ci    file->flags &= ~LFS_F_INLINE;
328219ea8026Sopenharmony_ci    return 0;
328319ea8026Sopenharmony_ci}
328419ea8026Sopenharmony_ci#endif
328519ea8026Sopenharmony_ci
328619ea8026Sopenharmony_cistatic int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) {
328719ea8026Sopenharmony_ci    if (file->flags & LFS_F_READING) {
328819ea8026Sopenharmony_ci        if (!(file->flags & LFS_F_INLINE)) {
328919ea8026Sopenharmony_ci            lfs_cache_drop(lfs, &file->cache);
329019ea8026Sopenharmony_ci        }
329119ea8026Sopenharmony_ci        file->flags &= ~LFS_F_READING;
329219ea8026Sopenharmony_ci    }
329319ea8026Sopenharmony_ci
329419ea8026Sopenharmony_ci#ifndef LFS_READONLY
329519ea8026Sopenharmony_ci    if (file->flags & LFS_F_WRITING) {
329619ea8026Sopenharmony_ci        lfs_off_t pos = file->pos;
329719ea8026Sopenharmony_ci
329819ea8026Sopenharmony_ci        if (!(file->flags & LFS_F_INLINE)) {
329919ea8026Sopenharmony_ci            // copy over anything after current branch
330019ea8026Sopenharmony_ci            lfs_file_t orig = {
330119ea8026Sopenharmony_ci                .ctz.head = file->ctz.head,
330219ea8026Sopenharmony_ci                .ctz.size = file->ctz.size,
330319ea8026Sopenharmony_ci                .flags = LFS_O_RDONLY,
330419ea8026Sopenharmony_ci                .pos = file->pos,
330519ea8026Sopenharmony_ci                .cache = lfs->rcache,
330619ea8026Sopenharmony_ci            };
330719ea8026Sopenharmony_ci            lfs_cache_drop(lfs, &lfs->rcache);
330819ea8026Sopenharmony_ci
330919ea8026Sopenharmony_ci            while (file->pos < file->ctz.size) {
331019ea8026Sopenharmony_ci                // copy over a byte at a time, leave it up to caching
331119ea8026Sopenharmony_ci                // to make this efficient
331219ea8026Sopenharmony_ci                uint8_t data;
331319ea8026Sopenharmony_ci                lfs_ssize_t res = lfs_file_flushedread(lfs, &orig, &data, 1);
331419ea8026Sopenharmony_ci                if (res < 0) {
331519ea8026Sopenharmony_ci                    return res;
331619ea8026Sopenharmony_ci                }
331719ea8026Sopenharmony_ci
331819ea8026Sopenharmony_ci                res = lfs_file_flushedwrite(lfs, file, &data, 1);
331919ea8026Sopenharmony_ci                if (res < 0) {
332019ea8026Sopenharmony_ci                    return res;
332119ea8026Sopenharmony_ci                }
332219ea8026Sopenharmony_ci
332319ea8026Sopenharmony_ci                // keep our reference to the rcache in sync
332419ea8026Sopenharmony_ci                if (lfs->rcache.block != LFS_BLOCK_NULL) {
332519ea8026Sopenharmony_ci                    lfs_cache_drop(lfs, &orig.cache);
332619ea8026Sopenharmony_ci                    lfs_cache_drop(lfs, &lfs->rcache);
332719ea8026Sopenharmony_ci                }
332819ea8026Sopenharmony_ci            }
332919ea8026Sopenharmony_ci
333019ea8026Sopenharmony_ci            // write out what we have
333119ea8026Sopenharmony_ci            while (true) {
333219ea8026Sopenharmony_ci                int err = lfs_bd_flush(lfs, &file->cache, &lfs->rcache, true);
333319ea8026Sopenharmony_ci                if (err) {
333419ea8026Sopenharmony_ci                    if (err == LFS_ERR_CORRUPT) {
333519ea8026Sopenharmony_ci                        goto relocate;
333619ea8026Sopenharmony_ci                    }
333719ea8026Sopenharmony_ci                    return err;
333819ea8026Sopenharmony_ci                }
333919ea8026Sopenharmony_ci
334019ea8026Sopenharmony_ci                break;
334119ea8026Sopenharmony_ci
334219ea8026Sopenharmony_cirelocate:
334319ea8026Sopenharmony_ci                LFS_DEBUG("Bad block at 0x%"PRIx32, file->block);
334419ea8026Sopenharmony_ci                err = lfs_file_relocate(lfs, file);
334519ea8026Sopenharmony_ci                if (err) {
334619ea8026Sopenharmony_ci                    return err;
334719ea8026Sopenharmony_ci                }
334819ea8026Sopenharmony_ci            }
334919ea8026Sopenharmony_ci        } else {
335019ea8026Sopenharmony_ci            file->pos = lfs_max(file->pos, file->ctz.size);
335119ea8026Sopenharmony_ci        }
335219ea8026Sopenharmony_ci
335319ea8026Sopenharmony_ci        // actual file updates
335419ea8026Sopenharmony_ci        file->ctz.head = file->block;
335519ea8026Sopenharmony_ci        file->ctz.size = file->pos;
335619ea8026Sopenharmony_ci        file->flags &= ~LFS_F_WRITING;
335719ea8026Sopenharmony_ci        file->flags |= LFS_F_DIRTY;
335819ea8026Sopenharmony_ci
335919ea8026Sopenharmony_ci        file->pos = pos;
336019ea8026Sopenharmony_ci    }
336119ea8026Sopenharmony_ci#endif
336219ea8026Sopenharmony_ci
336319ea8026Sopenharmony_ci    return 0;
336419ea8026Sopenharmony_ci}
336519ea8026Sopenharmony_ci
336619ea8026Sopenharmony_ci#ifndef LFS_READONLY
336719ea8026Sopenharmony_cistatic int lfs_file_rawsync(lfs_t *lfs, lfs_file_t *file) {
336819ea8026Sopenharmony_ci    if (file->flags & LFS_F_ERRED) {
336919ea8026Sopenharmony_ci        // it's not safe to do anything if our file errored
337019ea8026Sopenharmony_ci        return 0;
337119ea8026Sopenharmony_ci    }
337219ea8026Sopenharmony_ci
337319ea8026Sopenharmony_ci    int err = lfs_file_flush(lfs, file);
337419ea8026Sopenharmony_ci    if (err) {
337519ea8026Sopenharmony_ci        file->flags |= LFS_F_ERRED;
337619ea8026Sopenharmony_ci        return err;
337719ea8026Sopenharmony_ci    }
337819ea8026Sopenharmony_ci
337919ea8026Sopenharmony_ci
338019ea8026Sopenharmony_ci    if ((file->flags & LFS_F_DIRTY) &&
338119ea8026Sopenharmony_ci            !lfs_pair_isnull(file->m.pair)) {
338219ea8026Sopenharmony_ci        // update dir entry
338319ea8026Sopenharmony_ci        uint16_t type;
338419ea8026Sopenharmony_ci        const void *buffer;
338519ea8026Sopenharmony_ci        lfs_size_t size;
338619ea8026Sopenharmony_ci        struct lfs_ctz ctz;
338719ea8026Sopenharmony_ci        if (file->flags & LFS_F_INLINE) {
338819ea8026Sopenharmony_ci            // inline the whole file
338919ea8026Sopenharmony_ci            type = LFS_TYPE_INLINESTRUCT;
339019ea8026Sopenharmony_ci            buffer = file->cache.buffer;
339119ea8026Sopenharmony_ci            size = file->ctz.size;
339219ea8026Sopenharmony_ci        } else {
339319ea8026Sopenharmony_ci            // update the ctz reference
339419ea8026Sopenharmony_ci            type = LFS_TYPE_CTZSTRUCT;
339519ea8026Sopenharmony_ci            // copy ctz so alloc will work during a relocate
339619ea8026Sopenharmony_ci            ctz = file->ctz;
339719ea8026Sopenharmony_ci            lfs_ctz_tole32(&ctz);
339819ea8026Sopenharmony_ci            buffer = &ctz;
339919ea8026Sopenharmony_ci            size = sizeof(ctz);
340019ea8026Sopenharmony_ci        }
340119ea8026Sopenharmony_ci
340219ea8026Sopenharmony_ci        // commit file data and attributes
340319ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &file->m, LFS_MKATTRS(
340419ea8026Sopenharmony_ci                {LFS_MKTAG(type, file->id, size), buffer},
340519ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_FROM_USERATTRS, file->id,
340619ea8026Sopenharmony_ci                    file->cfg->attr_count), file->cfg->attrs}));
340719ea8026Sopenharmony_ci        if (err) {
340819ea8026Sopenharmony_ci            file->flags |= LFS_F_ERRED;
340919ea8026Sopenharmony_ci            return err;
341019ea8026Sopenharmony_ci        }
341119ea8026Sopenharmony_ci
341219ea8026Sopenharmony_ci        file->flags &= ~LFS_F_DIRTY;
341319ea8026Sopenharmony_ci    }
341419ea8026Sopenharmony_ci
341519ea8026Sopenharmony_ci    return 0;
341619ea8026Sopenharmony_ci}
341719ea8026Sopenharmony_ci#endif
341819ea8026Sopenharmony_ci
341919ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_flushedread(lfs_t *lfs, lfs_file_t *file,
342019ea8026Sopenharmony_ci        void *buffer, lfs_size_t size) {
342119ea8026Sopenharmony_ci    uint8_t *data = buffer;
342219ea8026Sopenharmony_ci    lfs_size_t nsize = size;
342319ea8026Sopenharmony_ci
342419ea8026Sopenharmony_ci    if (file->pos >= file->ctz.size) {
342519ea8026Sopenharmony_ci        // eof if past end
342619ea8026Sopenharmony_ci        return 0;
342719ea8026Sopenharmony_ci    }
342819ea8026Sopenharmony_ci
342919ea8026Sopenharmony_ci    size = lfs_min(size, file->ctz.size - file->pos);
343019ea8026Sopenharmony_ci    nsize = size;
343119ea8026Sopenharmony_ci
343219ea8026Sopenharmony_ci    while (nsize > 0) {
343319ea8026Sopenharmony_ci        // check if we need a new block
343419ea8026Sopenharmony_ci        if (!(file->flags & LFS_F_READING) ||
343519ea8026Sopenharmony_ci                file->off == lfs->cfg->block_size) {
343619ea8026Sopenharmony_ci            if (!(file->flags & LFS_F_INLINE)) {
343719ea8026Sopenharmony_ci                int err = lfs_ctz_find(lfs, NULL, &file->cache,
343819ea8026Sopenharmony_ci                        file->ctz.head, file->ctz.size,
343919ea8026Sopenharmony_ci                        file->pos, &file->block, &file->off);
344019ea8026Sopenharmony_ci                if (err) {
344119ea8026Sopenharmony_ci                    return err;
344219ea8026Sopenharmony_ci                }
344319ea8026Sopenharmony_ci            } else {
344419ea8026Sopenharmony_ci                file->block = LFS_BLOCK_INLINE;
344519ea8026Sopenharmony_ci                file->off = file->pos;
344619ea8026Sopenharmony_ci            }
344719ea8026Sopenharmony_ci
344819ea8026Sopenharmony_ci            file->flags |= LFS_F_READING;
344919ea8026Sopenharmony_ci        }
345019ea8026Sopenharmony_ci
345119ea8026Sopenharmony_ci        // read as much as we can in current block
345219ea8026Sopenharmony_ci        lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
345319ea8026Sopenharmony_ci        if (file->flags & LFS_F_INLINE) {
345419ea8026Sopenharmony_ci            int err = lfs_dir_getread(lfs, &file->m,
345519ea8026Sopenharmony_ci                    NULL, &file->cache, lfs->cfg->block_size,
345619ea8026Sopenharmony_ci                    LFS_MKTAG(0xfff, 0x1ff, 0),
345719ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_INLINESTRUCT, file->id, 0),
345819ea8026Sopenharmony_ci                    file->off, data, diff);
345919ea8026Sopenharmony_ci            if (err) {
346019ea8026Sopenharmony_ci                return err;
346119ea8026Sopenharmony_ci            }
346219ea8026Sopenharmony_ci        } else {
346319ea8026Sopenharmony_ci            int err = lfs_bd_read(lfs,
346419ea8026Sopenharmony_ci                    NULL, &file->cache, lfs->cfg->block_size,
346519ea8026Sopenharmony_ci                    file->block, file->off, data, diff);
346619ea8026Sopenharmony_ci            if (err) {
346719ea8026Sopenharmony_ci                return err;
346819ea8026Sopenharmony_ci            }
346919ea8026Sopenharmony_ci        }
347019ea8026Sopenharmony_ci
347119ea8026Sopenharmony_ci        file->pos += diff;
347219ea8026Sopenharmony_ci        file->off += diff;
347319ea8026Sopenharmony_ci        data += diff;
347419ea8026Sopenharmony_ci        nsize -= diff;
347519ea8026Sopenharmony_ci    }
347619ea8026Sopenharmony_ci
347719ea8026Sopenharmony_ci    return size;
347819ea8026Sopenharmony_ci}
347919ea8026Sopenharmony_ci
348019ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_rawread(lfs_t *lfs, lfs_file_t *file,
348119ea8026Sopenharmony_ci        void *buffer, lfs_size_t size) {
348219ea8026Sopenharmony_ci    LFS_ASSERT((file->flags & LFS_O_RDONLY) == LFS_O_RDONLY);
348319ea8026Sopenharmony_ci
348419ea8026Sopenharmony_ci#ifndef LFS_READONLY
348519ea8026Sopenharmony_ci    if (file->flags & LFS_F_WRITING) {
348619ea8026Sopenharmony_ci        // flush out any writes
348719ea8026Sopenharmony_ci        int err = lfs_file_flush(lfs, file);
348819ea8026Sopenharmony_ci        if (err) {
348919ea8026Sopenharmony_ci            return err;
349019ea8026Sopenharmony_ci        }
349119ea8026Sopenharmony_ci    }
349219ea8026Sopenharmony_ci#endif
349319ea8026Sopenharmony_ci
349419ea8026Sopenharmony_ci    return lfs_file_flushedread(lfs, file, buffer, size);
349519ea8026Sopenharmony_ci}
349619ea8026Sopenharmony_ci
349719ea8026Sopenharmony_ci
349819ea8026Sopenharmony_ci#ifndef LFS_READONLY
349919ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_flushedwrite(lfs_t *lfs, lfs_file_t *file,
350019ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
350119ea8026Sopenharmony_ci    const uint8_t *data = buffer;
350219ea8026Sopenharmony_ci    lfs_size_t nsize = size;
350319ea8026Sopenharmony_ci
350419ea8026Sopenharmony_ci    if ((file->flags & LFS_F_INLINE) &&
350519ea8026Sopenharmony_ci            lfs_max(file->pos+nsize, file->ctz.size) >
350619ea8026Sopenharmony_ci            lfs_min(0x3fe, lfs_min(
350719ea8026Sopenharmony_ci                lfs->cfg->cache_size,
350819ea8026Sopenharmony_ci                (lfs->cfg->metadata_max ?
350919ea8026Sopenharmony_ci                    lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) {
351019ea8026Sopenharmony_ci        // inline file doesn't fit anymore
351119ea8026Sopenharmony_ci        int err = lfs_file_outline(lfs, file);
351219ea8026Sopenharmony_ci        if (err) {
351319ea8026Sopenharmony_ci            file->flags |= LFS_F_ERRED;
351419ea8026Sopenharmony_ci            return err;
351519ea8026Sopenharmony_ci        }
351619ea8026Sopenharmony_ci    }
351719ea8026Sopenharmony_ci
351819ea8026Sopenharmony_ci    while (nsize > 0) {
351919ea8026Sopenharmony_ci        // check if we need a new block
352019ea8026Sopenharmony_ci        if (!(file->flags & LFS_F_WRITING) ||
352119ea8026Sopenharmony_ci                file->off == lfs->cfg->block_size) {
352219ea8026Sopenharmony_ci            if (!(file->flags & LFS_F_INLINE)) {
352319ea8026Sopenharmony_ci                if (!(file->flags & LFS_F_WRITING) && file->pos > 0) {
352419ea8026Sopenharmony_ci                    // find out which block we're extending from
352519ea8026Sopenharmony_ci                    int err = lfs_ctz_find(lfs, NULL, &file->cache,
352619ea8026Sopenharmony_ci                            file->ctz.head, file->ctz.size,
352719ea8026Sopenharmony_ci                            file->pos-1, &file->block, &(lfs_off_t){0});
352819ea8026Sopenharmony_ci                    if (err) {
352919ea8026Sopenharmony_ci                        file->flags |= LFS_F_ERRED;
353019ea8026Sopenharmony_ci                        return err;
353119ea8026Sopenharmony_ci                    }
353219ea8026Sopenharmony_ci
353319ea8026Sopenharmony_ci                    // mark cache as dirty since we may have read data into it
353419ea8026Sopenharmony_ci                    lfs_cache_zero(lfs, &file->cache);
353519ea8026Sopenharmony_ci                }
353619ea8026Sopenharmony_ci
353719ea8026Sopenharmony_ci                // extend file with new blocks
353819ea8026Sopenharmony_ci                lfs_alloc_ack(lfs);
353919ea8026Sopenharmony_ci                int err = lfs_ctz_extend(lfs, &file->cache, &lfs->rcache,
354019ea8026Sopenharmony_ci                        file->block, file->pos,
354119ea8026Sopenharmony_ci                        &file->block, &file->off);
354219ea8026Sopenharmony_ci                if (err) {
354319ea8026Sopenharmony_ci                    file->flags |= LFS_F_ERRED;
354419ea8026Sopenharmony_ci                    return err;
354519ea8026Sopenharmony_ci                }
354619ea8026Sopenharmony_ci            } else {
354719ea8026Sopenharmony_ci                file->block = LFS_BLOCK_INLINE;
354819ea8026Sopenharmony_ci                file->off = file->pos;
354919ea8026Sopenharmony_ci            }
355019ea8026Sopenharmony_ci
355119ea8026Sopenharmony_ci            file->flags |= LFS_F_WRITING;
355219ea8026Sopenharmony_ci        }
355319ea8026Sopenharmony_ci
355419ea8026Sopenharmony_ci        // program as much as we can in current block
355519ea8026Sopenharmony_ci        lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->off);
355619ea8026Sopenharmony_ci        while (true) {
355719ea8026Sopenharmony_ci            int err = lfs_bd_prog(lfs, &file->cache, &lfs->rcache, true,
355819ea8026Sopenharmony_ci                    file->block, file->off, data, diff);
355919ea8026Sopenharmony_ci            if (err) {
356019ea8026Sopenharmony_ci                if (err == LFS_ERR_CORRUPT) {
356119ea8026Sopenharmony_ci                    goto relocate;
356219ea8026Sopenharmony_ci                }
356319ea8026Sopenharmony_ci                file->flags |= LFS_F_ERRED;
356419ea8026Sopenharmony_ci                return err;
356519ea8026Sopenharmony_ci            }
356619ea8026Sopenharmony_ci
356719ea8026Sopenharmony_ci            break;
356819ea8026Sopenharmony_cirelocate:
356919ea8026Sopenharmony_ci            err = lfs_file_relocate(lfs, file);
357019ea8026Sopenharmony_ci            if (err) {
357119ea8026Sopenharmony_ci                file->flags |= LFS_F_ERRED;
357219ea8026Sopenharmony_ci                return err;
357319ea8026Sopenharmony_ci            }
357419ea8026Sopenharmony_ci        }
357519ea8026Sopenharmony_ci
357619ea8026Sopenharmony_ci        file->pos += diff;
357719ea8026Sopenharmony_ci        file->off += diff;
357819ea8026Sopenharmony_ci        data += diff;
357919ea8026Sopenharmony_ci        nsize -= diff;
358019ea8026Sopenharmony_ci
358119ea8026Sopenharmony_ci        lfs_alloc_ack(lfs);
358219ea8026Sopenharmony_ci    }
358319ea8026Sopenharmony_ci
358419ea8026Sopenharmony_ci    return size;
358519ea8026Sopenharmony_ci}
358619ea8026Sopenharmony_ci
358719ea8026Sopenharmony_cistatic lfs_ssize_t lfs_file_rawwrite(lfs_t *lfs, lfs_file_t *file,
358819ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
358919ea8026Sopenharmony_ci    LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY);
359019ea8026Sopenharmony_ci
359119ea8026Sopenharmony_ci    if (file->flags & LFS_F_READING) {
359219ea8026Sopenharmony_ci        // drop any reads
359319ea8026Sopenharmony_ci        int err = lfs_file_flush(lfs, file);
359419ea8026Sopenharmony_ci        if (err) {
359519ea8026Sopenharmony_ci            return err;
359619ea8026Sopenharmony_ci        }
359719ea8026Sopenharmony_ci    }
359819ea8026Sopenharmony_ci
359919ea8026Sopenharmony_ci    if ((file->flags & LFS_O_APPEND) && file->pos < file->ctz.size) {
360019ea8026Sopenharmony_ci        file->pos = file->ctz.size;
360119ea8026Sopenharmony_ci    }
360219ea8026Sopenharmony_ci
360319ea8026Sopenharmony_ci    if (file->pos + size > lfs->file_max) {
360419ea8026Sopenharmony_ci        // Larger than file limit?
360519ea8026Sopenharmony_ci        return LFS_ERR_FBIG;
360619ea8026Sopenharmony_ci    }
360719ea8026Sopenharmony_ci
360819ea8026Sopenharmony_ci    if (!(file->flags & LFS_F_WRITING) && file->pos > file->ctz.size) {
360919ea8026Sopenharmony_ci        // fill with zeros
361019ea8026Sopenharmony_ci        lfs_off_t pos = file->pos;
361119ea8026Sopenharmony_ci        file->pos = file->ctz.size;
361219ea8026Sopenharmony_ci
361319ea8026Sopenharmony_ci        while (file->pos < pos) {
361419ea8026Sopenharmony_ci            lfs_ssize_t res = lfs_file_flushedwrite(lfs, file, &(uint8_t){0}, 1);
361519ea8026Sopenharmony_ci            if (res < 0) {
361619ea8026Sopenharmony_ci                return res;
361719ea8026Sopenharmony_ci            }
361819ea8026Sopenharmony_ci        }
361919ea8026Sopenharmony_ci    }
362019ea8026Sopenharmony_ci
362119ea8026Sopenharmony_ci    lfs_ssize_t nsize = lfs_file_flushedwrite(lfs, file, buffer, size);
362219ea8026Sopenharmony_ci    if (nsize < 0) {
362319ea8026Sopenharmony_ci        return nsize;
362419ea8026Sopenharmony_ci    }
362519ea8026Sopenharmony_ci
362619ea8026Sopenharmony_ci    file->flags &= ~LFS_F_ERRED;
362719ea8026Sopenharmony_ci    return nsize;
362819ea8026Sopenharmony_ci}
362919ea8026Sopenharmony_ci#endif
363019ea8026Sopenharmony_ci
363119ea8026Sopenharmony_cistatic lfs_soff_t lfs_file_rawseek(lfs_t *lfs, lfs_file_t *file,
363219ea8026Sopenharmony_ci        lfs_soff_t off, int whence) {
363319ea8026Sopenharmony_ci    // find new pos
363419ea8026Sopenharmony_ci    lfs_off_t npos = file->pos;
363519ea8026Sopenharmony_ci    if (whence == LFS_SEEK_SET) {
363619ea8026Sopenharmony_ci        npos = off;
363719ea8026Sopenharmony_ci    } else if (whence == LFS_SEEK_CUR) {
363819ea8026Sopenharmony_ci        if ((lfs_soff_t)file->pos + off < 0) {
363919ea8026Sopenharmony_ci            return LFS_ERR_INVAL;
364019ea8026Sopenharmony_ci        } else {
364119ea8026Sopenharmony_ci            npos = file->pos + off;
364219ea8026Sopenharmony_ci        }
364319ea8026Sopenharmony_ci    } else if (whence == LFS_SEEK_END) {
364419ea8026Sopenharmony_ci        lfs_soff_t res = lfs_file_rawsize(lfs, file) + off;
364519ea8026Sopenharmony_ci        if (res < 0) {
364619ea8026Sopenharmony_ci            return LFS_ERR_INVAL;
364719ea8026Sopenharmony_ci        } else {
364819ea8026Sopenharmony_ci            npos = res;
364919ea8026Sopenharmony_ci        }
365019ea8026Sopenharmony_ci    }
365119ea8026Sopenharmony_ci
365219ea8026Sopenharmony_ci    if (npos > lfs->file_max) {
365319ea8026Sopenharmony_ci        // file position out of range
365419ea8026Sopenharmony_ci        return LFS_ERR_INVAL;
365519ea8026Sopenharmony_ci    }
365619ea8026Sopenharmony_ci
365719ea8026Sopenharmony_ci    if (file->pos == npos) {
365819ea8026Sopenharmony_ci        // noop - position has not changed
365919ea8026Sopenharmony_ci        return npos;
366019ea8026Sopenharmony_ci    }
366119ea8026Sopenharmony_ci
366219ea8026Sopenharmony_ci    // if we're only reading and our new offset is still in the file's cache
366319ea8026Sopenharmony_ci    // we can avoid flushing and needing to reread the data
366419ea8026Sopenharmony_ci    if (
366519ea8026Sopenharmony_ci#ifndef LFS_READONLY
366619ea8026Sopenharmony_ci        !(file->flags & LFS_F_WRITING)
366719ea8026Sopenharmony_ci#else
366819ea8026Sopenharmony_ci        true
366919ea8026Sopenharmony_ci#endif
367019ea8026Sopenharmony_ci            ) {
367119ea8026Sopenharmony_ci        int oindex = lfs_ctz_index(lfs, &(lfs_off_t){file->pos});
367219ea8026Sopenharmony_ci        lfs_off_t noff = npos;
367319ea8026Sopenharmony_ci        int nindex = lfs_ctz_index(lfs, &noff);
367419ea8026Sopenharmony_ci        if (oindex == nindex
367519ea8026Sopenharmony_ci                && noff >= file->cache.off
367619ea8026Sopenharmony_ci                && noff < file->cache.off + file->cache.size) {
367719ea8026Sopenharmony_ci            file->pos = npos;
367819ea8026Sopenharmony_ci            file->off = noff;
367919ea8026Sopenharmony_ci            return npos;
368019ea8026Sopenharmony_ci        }
368119ea8026Sopenharmony_ci    }
368219ea8026Sopenharmony_ci
368319ea8026Sopenharmony_ci    // write out everything beforehand, may be noop if rdonly
368419ea8026Sopenharmony_ci    int err = lfs_file_flush(lfs, file);
368519ea8026Sopenharmony_ci    if (err) {
368619ea8026Sopenharmony_ci        return err;
368719ea8026Sopenharmony_ci    }
368819ea8026Sopenharmony_ci
368919ea8026Sopenharmony_ci    // update pos
369019ea8026Sopenharmony_ci    file->pos = npos;
369119ea8026Sopenharmony_ci    return npos;
369219ea8026Sopenharmony_ci}
369319ea8026Sopenharmony_ci
369419ea8026Sopenharmony_ci#ifndef LFS_READONLY
369519ea8026Sopenharmony_cistatic int lfs_file_rawtruncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
369619ea8026Sopenharmony_ci    LFS_ASSERT((file->flags & LFS_O_WRONLY) == LFS_O_WRONLY);
369719ea8026Sopenharmony_ci
369819ea8026Sopenharmony_ci    if (size > LFS_FILE_MAX) {
369919ea8026Sopenharmony_ci        return LFS_ERR_INVAL;
370019ea8026Sopenharmony_ci    }
370119ea8026Sopenharmony_ci
370219ea8026Sopenharmony_ci    lfs_off_t pos = file->pos;
370319ea8026Sopenharmony_ci    lfs_off_t oldsize = lfs_file_rawsize(lfs, file);
370419ea8026Sopenharmony_ci    if (size < oldsize) {
370519ea8026Sopenharmony_ci        // revert to inline file?
370619ea8026Sopenharmony_ci        if (size <= lfs_min(0x3fe, lfs_min(
370719ea8026Sopenharmony_ci                lfs->cfg->cache_size,
370819ea8026Sopenharmony_ci                (lfs->cfg->metadata_max ?
370919ea8026Sopenharmony_ci                    lfs->cfg->metadata_max : lfs->cfg->block_size) / 8))) {
371019ea8026Sopenharmony_ci            // flush+seek to head
371119ea8026Sopenharmony_ci            lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET);
371219ea8026Sopenharmony_ci            if (res < 0) {
371319ea8026Sopenharmony_ci                return (int)res;
371419ea8026Sopenharmony_ci            }
371519ea8026Sopenharmony_ci
371619ea8026Sopenharmony_ci            // read our data into rcache temporarily
371719ea8026Sopenharmony_ci            lfs_cache_drop(lfs, &lfs->rcache);
371819ea8026Sopenharmony_ci            res = lfs_file_flushedread(lfs, file,
371919ea8026Sopenharmony_ci                    lfs->rcache.buffer, size);
372019ea8026Sopenharmony_ci            if (res < 0) {
372119ea8026Sopenharmony_ci                return (int)res;
372219ea8026Sopenharmony_ci            }
372319ea8026Sopenharmony_ci
372419ea8026Sopenharmony_ci            file->ctz.head = LFS_BLOCK_INLINE;
372519ea8026Sopenharmony_ci            file->ctz.size = size;
372619ea8026Sopenharmony_ci            file->flags |= LFS_F_DIRTY | LFS_F_READING | LFS_F_INLINE;
372719ea8026Sopenharmony_ci            file->cache.block = file->ctz.head;
372819ea8026Sopenharmony_ci            file->cache.off = 0;
372919ea8026Sopenharmony_ci            file->cache.size = lfs->cfg->cache_size;
373019ea8026Sopenharmony_ci            memcpy(file->cache.buffer, lfs->rcache.buffer, size);
373119ea8026Sopenharmony_ci
373219ea8026Sopenharmony_ci        } else {
373319ea8026Sopenharmony_ci            // need to flush since directly changing metadata
373419ea8026Sopenharmony_ci            int err = lfs_file_flush(lfs, file);
373519ea8026Sopenharmony_ci            if (err) {
373619ea8026Sopenharmony_ci                return err;
373719ea8026Sopenharmony_ci            }
373819ea8026Sopenharmony_ci
373919ea8026Sopenharmony_ci            // lookup new head in ctz skip list
374019ea8026Sopenharmony_ci            err = lfs_ctz_find(lfs, NULL, &file->cache,
374119ea8026Sopenharmony_ci                    file->ctz.head, file->ctz.size,
374219ea8026Sopenharmony_ci                    size-1, &file->block, &(lfs_off_t){0});
374319ea8026Sopenharmony_ci            if (err) {
374419ea8026Sopenharmony_ci                return err;
374519ea8026Sopenharmony_ci            }
374619ea8026Sopenharmony_ci
374719ea8026Sopenharmony_ci            // need to set pos/block/off consistently so seeking back to
374819ea8026Sopenharmony_ci            // the old position does not get confused
374919ea8026Sopenharmony_ci            file->pos = size;
375019ea8026Sopenharmony_ci            file->ctz.head = file->block;
375119ea8026Sopenharmony_ci            file->ctz.size = size;
375219ea8026Sopenharmony_ci            file->flags |= LFS_F_DIRTY | LFS_F_READING;
375319ea8026Sopenharmony_ci        }
375419ea8026Sopenharmony_ci    } else if (size > oldsize) {
375519ea8026Sopenharmony_ci        // flush+seek if not already at end
375619ea8026Sopenharmony_ci        lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_END);
375719ea8026Sopenharmony_ci        if (res < 0) {
375819ea8026Sopenharmony_ci            return (int)res;
375919ea8026Sopenharmony_ci        }
376019ea8026Sopenharmony_ci
376119ea8026Sopenharmony_ci        // fill with zeros
376219ea8026Sopenharmony_ci        while (file->pos < size) {
376319ea8026Sopenharmony_ci            res = lfs_file_rawwrite(lfs, file, &(uint8_t){0}, 1);
376419ea8026Sopenharmony_ci            if (res < 0) {
376519ea8026Sopenharmony_ci                return (int)res;
376619ea8026Sopenharmony_ci            }
376719ea8026Sopenharmony_ci        }
376819ea8026Sopenharmony_ci    }
376919ea8026Sopenharmony_ci
377019ea8026Sopenharmony_ci    // restore pos
377119ea8026Sopenharmony_ci    lfs_soff_t res = lfs_file_rawseek(lfs, file, pos, LFS_SEEK_SET);
377219ea8026Sopenharmony_ci    if (res < 0) {
377319ea8026Sopenharmony_ci      return (int)res;
377419ea8026Sopenharmony_ci    }
377519ea8026Sopenharmony_ci
377619ea8026Sopenharmony_ci    return 0;
377719ea8026Sopenharmony_ci}
377819ea8026Sopenharmony_ci#endif
377919ea8026Sopenharmony_ci
378019ea8026Sopenharmony_cistatic lfs_soff_t lfs_file_rawtell(lfs_t *lfs, lfs_file_t *file) {
378119ea8026Sopenharmony_ci    (void)lfs;
378219ea8026Sopenharmony_ci    return file->pos;
378319ea8026Sopenharmony_ci}
378419ea8026Sopenharmony_ci
378519ea8026Sopenharmony_cistatic int lfs_file_rawrewind(lfs_t *lfs, lfs_file_t *file) {
378619ea8026Sopenharmony_ci    lfs_soff_t res = lfs_file_rawseek(lfs, file, 0, LFS_SEEK_SET);
378719ea8026Sopenharmony_ci    if (res < 0) {
378819ea8026Sopenharmony_ci        return (int)res;
378919ea8026Sopenharmony_ci    }
379019ea8026Sopenharmony_ci
379119ea8026Sopenharmony_ci    return 0;
379219ea8026Sopenharmony_ci}
379319ea8026Sopenharmony_ci
379419ea8026Sopenharmony_cistatic lfs_soff_t lfs_file_rawsize(lfs_t *lfs, lfs_file_t *file) {
379519ea8026Sopenharmony_ci    (void)lfs;
379619ea8026Sopenharmony_ci
379719ea8026Sopenharmony_ci#ifndef LFS_READONLY
379819ea8026Sopenharmony_ci    if (file->flags & LFS_F_WRITING) {
379919ea8026Sopenharmony_ci        return lfs_max(file->pos, file->ctz.size);
380019ea8026Sopenharmony_ci    }
380119ea8026Sopenharmony_ci#endif
380219ea8026Sopenharmony_ci
380319ea8026Sopenharmony_ci    return file->ctz.size;
380419ea8026Sopenharmony_ci}
380519ea8026Sopenharmony_ci
380619ea8026Sopenharmony_ci
380719ea8026Sopenharmony_ci/// General fs operations ///
380819ea8026Sopenharmony_cistatic int lfs_rawstat(lfs_t *lfs, const char *path, struct lfs_info *info) {
380919ea8026Sopenharmony_ci    lfs_mdir_t cwd;
381019ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
381119ea8026Sopenharmony_ci    if (tag < 0) {
381219ea8026Sopenharmony_ci        return (int)tag;
381319ea8026Sopenharmony_ci    }
381419ea8026Sopenharmony_ci
381519ea8026Sopenharmony_ci    return lfs_dir_getinfo(lfs, &cwd, lfs_tag_id(tag), info);
381619ea8026Sopenharmony_ci}
381719ea8026Sopenharmony_ci
381819ea8026Sopenharmony_ci#ifndef LFS_READONLY
381919ea8026Sopenharmony_cistatic int lfs_rawremove(lfs_t *lfs, const char *path) {
382019ea8026Sopenharmony_ci    // deorphan if we haven't yet, needed at most once after poweron
382119ea8026Sopenharmony_ci    int err = lfs_fs_forceconsistency(lfs);
382219ea8026Sopenharmony_ci    if (err) {
382319ea8026Sopenharmony_ci        return err;
382419ea8026Sopenharmony_ci    }
382519ea8026Sopenharmony_ci
382619ea8026Sopenharmony_ci    lfs_mdir_t cwd;
382719ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
382819ea8026Sopenharmony_ci    if (tag < 0 || lfs_tag_id(tag) == 0x3ff) {
382919ea8026Sopenharmony_ci        return (tag < 0) ? (int)tag : LFS_ERR_INVAL;
383019ea8026Sopenharmony_ci    }
383119ea8026Sopenharmony_ci
383219ea8026Sopenharmony_ci    struct lfs_mlist dir;
383319ea8026Sopenharmony_ci    dir.next = lfs->mlist;
383419ea8026Sopenharmony_ci    if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
383519ea8026Sopenharmony_ci        // must be empty before removal
383619ea8026Sopenharmony_ci        lfs_block_t pair[2];
383719ea8026Sopenharmony_ci        lfs_stag_t res = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x700, 0x3ff, 0),
383819ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_STRUCT, lfs_tag_id(tag), 8), pair);
383919ea8026Sopenharmony_ci        if (res < 0) {
384019ea8026Sopenharmony_ci            return (int)res;
384119ea8026Sopenharmony_ci        }
384219ea8026Sopenharmony_ci        lfs_pair_fromle32(pair);
384319ea8026Sopenharmony_ci
384419ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &dir.m, pair);
384519ea8026Sopenharmony_ci        if (err) {
384619ea8026Sopenharmony_ci            return err;
384719ea8026Sopenharmony_ci        }
384819ea8026Sopenharmony_ci
384919ea8026Sopenharmony_ci        if (dir.m.count > 0 || dir.m.split) {
385019ea8026Sopenharmony_ci            return LFS_ERR_NOTEMPTY;
385119ea8026Sopenharmony_ci        }
385219ea8026Sopenharmony_ci
385319ea8026Sopenharmony_ci        // mark fs as orphaned
385419ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, +1);
385519ea8026Sopenharmony_ci        if (err) {
385619ea8026Sopenharmony_ci            return err;
385719ea8026Sopenharmony_ci        }
385819ea8026Sopenharmony_ci
385919ea8026Sopenharmony_ci        // I know it's crazy but yes, dir can be changed by our parent's
386019ea8026Sopenharmony_ci        // commit (if predecessor is child)
386119ea8026Sopenharmony_ci        dir.type = 0;
386219ea8026Sopenharmony_ci        dir.id = 0;
386319ea8026Sopenharmony_ci        lfs->mlist = &dir;
386419ea8026Sopenharmony_ci    }
386519ea8026Sopenharmony_ci
386619ea8026Sopenharmony_ci    // delete the entry
386719ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &cwd, LFS_MKATTRS(
386819ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(tag), 0), NULL}));
386919ea8026Sopenharmony_ci    if (err) {
387019ea8026Sopenharmony_ci        lfs->mlist = dir.next;
387119ea8026Sopenharmony_ci        return err;
387219ea8026Sopenharmony_ci    }
387319ea8026Sopenharmony_ci
387419ea8026Sopenharmony_ci    lfs->mlist = dir.next;
387519ea8026Sopenharmony_ci    if (lfs_tag_type3(tag) == LFS_TYPE_DIR) {
387619ea8026Sopenharmony_ci        // fix orphan
387719ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, -1);
387819ea8026Sopenharmony_ci        if (err) {
387919ea8026Sopenharmony_ci            return err;
388019ea8026Sopenharmony_ci        }
388119ea8026Sopenharmony_ci
388219ea8026Sopenharmony_ci        err = lfs_fs_pred(lfs, dir.m.pair, &cwd);
388319ea8026Sopenharmony_ci        if (err) {
388419ea8026Sopenharmony_ci            return err;
388519ea8026Sopenharmony_ci        }
388619ea8026Sopenharmony_ci
388719ea8026Sopenharmony_ci        err = lfs_dir_drop(lfs, &cwd, &dir.m);
388819ea8026Sopenharmony_ci        if (err) {
388919ea8026Sopenharmony_ci            return err;
389019ea8026Sopenharmony_ci        }
389119ea8026Sopenharmony_ci    }
389219ea8026Sopenharmony_ci
389319ea8026Sopenharmony_ci    return 0;
389419ea8026Sopenharmony_ci}
389519ea8026Sopenharmony_ci#endif
389619ea8026Sopenharmony_ci
389719ea8026Sopenharmony_ci#ifndef LFS_READONLY
389819ea8026Sopenharmony_cistatic int lfs_rawrename(lfs_t *lfs, const char *oldpath, const char *newpath) {
389919ea8026Sopenharmony_ci    // deorphan if we haven't yet, needed at most once after poweron
390019ea8026Sopenharmony_ci    int err = lfs_fs_forceconsistency(lfs);
390119ea8026Sopenharmony_ci    if (err) {
390219ea8026Sopenharmony_ci        return err;
390319ea8026Sopenharmony_ci    }
390419ea8026Sopenharmony_ci
390519ea8026Sopenharmony_ci    // find old entry
390619ea8026Sopenharmony_ci    lfs_mdir_t oldcwd;
390719ea8026Sopenharmony_ci    lfs_stag_t oldtag = lfs_dir_find(lfs, &oldcwd, &oldpath, NULL);
390819ea8026Sopenharmony_ci    if (oldtag < 0 || lfs_tag_id(oldtag) == 0x3ff) {
390919ea8026Sopenharmony_ci        return (oldtag < 0) ? (int)oldtag : LFS_ERR_INVAL;
391019ea8026Sopenharmony_ci    }
391119ea8026Sopenharmony_ci
391219ea8026Sopenharmony_ci    // find new entry
391319ea8026Sopenharmony_ci    lfs_mdir_t newcwd;
391419ea8026Sopenharmony_ci    uint16_t newid;
391519ea8026Sopenharmony_ci    lfs_stag_t prevtag = lfs_dir_find(lfs, &newcwd, &newpath, &newid);
391619ea8026Sopenharmony_ci    if ((prevtag < 0 || lfs_tag_id(prevtag) == 0x3ff) &&
391719ea8026Sopenharmony_ci            !(prevtag == LFS_ERR_NOENT && newid != 0x3ff)) {
391819ea8026Sopenharmony_ci        return (prevtag < 0) ? (int)prevtag : LFS_ERR_INVAL;
391919ea8026Sopenharmony_ci    }
392019ea8026Sopenharmony_ci
392119ea8026Sopenharmony_ci    // if we're in the same pair there's a few special cases...
392219ea8026Sopenharmony_ci    bool samepair = (lfs_pair_cmp(oldcwd.pair, newcwd.pair) == 0);
392319ea8026Sopenharmony_ci    uint16_t newoldid = lfs_tag_id(oldtag);
392419ea8026Sopenharmony_ci
392519ea8026Sopenharmony_ci    struct lfs_mlist prevdir;
392619ea8026Sopenharmony_ci    prevdir.next = lfs->mlist;
392719ea8026Sopenharmony_ci    if (prevtag == LFS_ERR_NOENT) {
392819ea8026Sopenharmony_ci        // check that name fits
392919ea8026Sopenharmony_ci        lfs_size_t nlen = strlen(newpath);
393019ea8026Sopenharmony_ci        if (nlen > lfs->name_max) {
393119ea8026Sopenharmony_ci            return LFS_ERR_NAMETOOLONG;
393219ea8026Sopenharmony_ci        }
393319ea8026Sopenharmony_ci
393419ea8026Sopenharmony_ci        // there is a small chance we are being renamed in the same
393519ea8026Sopenharmony_ci        // directory/ to an id less than our old id, the global update
393619ea8026Sopenharmony_ci        // to handle this is a bit messy
393719ea8026Sopenharmony_ci        if (samepair && newid <= newoldid) {
393819ea8026Sopenharmony_ci            newoldid += 1;
393919ea8026Sopenharmony_ci        }
394019ea8026Sopenharmony_ci    } else if (lfs_tag_type3(prevtag) != lfs_tag_type3(oldtag)) {
394119ea8026Sopenharmony_ci        return LFS_ERR_ISDIR;
394219ea8026Sopenharmony_ci    } else if (samepair && newid == newoldid) {
394319ea8026Sopenharmony_ci        // we're renaming to ourselves??
394419ea8026Sopenharmony_ci        return 0;
394519ea8026Sopenharmony_ci    } else if (lfs_tag_type3(prevtag) == LFS_TYPE_DIR) {
394619ea8026Sopenharmony_ci        // must be empty before removal
394719ea8026Sopenharmony_ci        lfs_block_t prevpair[2];
394819ea8026Sopenharmony_ci        lfs_stag_t res = lfs_dir_get(lfs, &newcwd, LFS_MKTAG(0x700, 0x3ff, 0),
394919ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_STRUCT, newid, 8), prevpair);
395019ea8026Sopenharmony_ci        if (res < 0) {
395119ea8026Sopenharmony_ci            return (int)res;
395219ea8026Sopenharmony_ci        }
395319ea8026Sopenharmony_ci        lfs_pair_fromle32(prevpair);
395419ea8026Sopenharmony_ci
395519ea8026Sopenharmony_ci        // must be empty before removal
395619ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &prevdir.m, prevpair);
395719ea8026Sopenharmony_ci        if (err) {
395819ea8026Sopenharmony_ci            return err;
395919ea8026Sopenharmony_ci        }
396019ea8026Sopenharmony_ci
396119ea8026Sopenharmony_ci        if (prevdir.m.count > 0 || prevdir.m.split) {
396219ea8026Sopenharmony_ci            return LFS_ERR_NOTEMPTY;
396319ea8026Sopenharmony_ci        }
396419ea8026Sopenharmony_ci
396519ea8026Sopenharmony_ci        // mark fs as orphaned
396619ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, +1);
396719ea8026Sopenharmony_ci        if (err) {
396819ea8026Sopenharmony_ci            return err;
396919ea8026Sopenharmony_ci        }
397019ea8026Sopenharmony_ci
397119ea8026Sopenharmony_ci        // I know it's crazy but yes, dir can be changed by our parent's
397219ea8026Sopenharmony_ci        // commit (if predecessor is child)
397319ea8026Sopenharmony_ci        prevdir.type = 0;
397419ea8026Sopenharmony_ci        prevdir.id = 0;
397519ea8026Sopenharmony_ci        lfs->mlist = &prevdir;
397619ea8026Sopenharmony_ci    }
397719ea8026Sopenharmony_ci
397819ea8026Sopenharmony_ci    if (!samepair) {
397919ea8026Sopenharmony_ci        lfs_fs_prepmove(lfs, newoldid, oldcwd.pair);
398019ea8026Sopenharmony_ci    }
398119ea8026Sopenharmony_ci
398219ea8026Sopenharmony_ci    // move over all attributes
398319ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &newcwd, LFS_MKATTRS(
398419ea8026Sopenharmony_ci            {LFS_MKTAG_IF(prevtag != LFS_ERR_NOENT,
398519ea8026Sopenharmony_ci                LFS_TYPE_DELETE, newid, 0), NULL},
398619ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_CREATE, newid, 0), NULL},
398719ea8026Sopenharmony_ci            {LFS_MKTAG(lfs_tag_type3(oldtag), newid, strlen(newpath)), newpath},
398819ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_FROM_MOVE, newid, lfs_tag_id(oldtag)), &oldcwd},
398919ea8026Sopenharmony_ci            {LFS_MKTAG_IF(samepair,
399019ea8026Sopenharmony_ci                LFS_TYPE_DELETE, newoldid, 0), NULL}));
399119ea8026Sopenharmony_ci    if (err) {
399219ea8026Sopenharmony_ci        lfs->mlist = prevdir.next;
399319ea8026Sopenharmony_ci        return err;
399419ea8026Sopenharmony_ci    }
399519ea8026Sopenharmony_ci
399619ea8026Sopenharmony_ci    // let commit clean up after move (if we're different! otherwise move
399719ea8026Sopenharmony_ci    // logic already fixed it for us)
399819ea8026Sopenharmony_ci    if (!samepair && lfs_gstate_hasmove(&lfs->gstate)) {
399919ea8026Sopenharmony_ci        // prep gstate and delete move id
400019ea8026Sopenharmony_ci        lfs_fs_prepmove(lfs, 0x3ff, NULL);
400119ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &oldcwd, LFS_MKATTRS(
400219ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_DELETE, lfs_tag_id(oldtag), 0), NULL}));
400319ea8026Sopenharmony_ci        if (err) {
400419ea8026Sopenharmony_ci            lfs->mlist = prevdir.next;
400519ea8026Sopenharmony_ci            return err;
400619ea8026Sopenharmony_ci        }
400719ea8026Sopenharmony_ci    }
400819ea8026Sopenharmony_ci
400919ea8026Sopenharmony_ci    lfs->mlist = prevdir.next;
401019ea8026Sopenharmony_ci    if (prevtag != LFS_ERR_NOENT
401119ea8026Sopenharmony_ci            && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) {
401219ea8026Sopenharmony_ci        // fix orphan
401319ea8026Sopenharmony_ci        err = lfs_fs_preporphans(lfs, -1);
401419ea8026Sopenharmony_ci        if (err) {
401519ea8026Sopenharmony_ci            return err;
401619ea8026Sopenharmony_ci        }
401719ea8026Sopenharmony_ci
401819ea8026Sopenharmony_ci        err = lfs_fs_pred(lfs, prevdir.m.pair, &newcwd);
401919ea8026Sopenharmony_ci        if (err) {
402019ea8026Sopenharmony_ci            return err;
402119ea8026Sopenharmony_ci        }
402219ea8026Sopenharmony_ci
402319ea8026Sopenharmony_ci        err = lfs_dir_drop(lfs, &newcwd, &prevdir.m);
402419ea8026Sopenharmony_ci        if (err) {
402519ea8026Sopenharmony_ci            return err;
402619ea8026Sopenharmony_ci        }
402719ea8026Sopenharmony_ci    }
402819ea8026Sopenharmony_ci
402919ea8026Sopenharmony_ci    return 0;
403019ea8026Sopenharmony_ci}
403119ea8026Sopenharmony_ci#endif
403219ea8026Sopenharmony_ci
403319ea8026Sopenharmony_cistatic lfs_ssize_t lfs_rawgetattr(lfs_t *lfs, const char *path,
403419ea8026Sopenharmony_ci        uint8_t type, void *buffer, lfs_size_t size) {
403519ea8026Sopenharmony_ci    lfs_mdir_t cwd;
403619ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
403719ea8026Sopenharmony_ci    if (tag < 0) {
403819ea8026Sopenharmony_ci        return tag;
403919ea8026Sopenharmony_ci    }
404019ea8026Sopenharmony_ci
404119ea8026Sopenharmony_ci    uint16_t id = lfs_tag_id(tag);
404219ea8026Sopenharmony_ci    if (id == 0x3ff) {
404319ea8026Sopenharmony_ci        // special case for root
404419ea8026Sopenharmony_ci        id = 0;
404519ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
404619ea8026Sopenharmony_ci        if (err) {
404719ea8026Sopenharmony_ci            return err;
404819ea8026Sopenharmony_ci        }
404919ea8026Sopenharmony_ci    }
405019ea8026Sopenharmony_ci
405119ea8026Sopenharmony_ci    tag = lfs_dir_get(lfs, &cwd, LFS_MKTAG(0x7ff, 0x3ff, 0),
405219ea8026Sopenharmony_ci            LFS_MKTAG(LFS_TYPE_USERATTR + type,
405319ea8026Sopenharmony_ci                id, lfs_min(size, lfs->attr_max)),
405419ea8026Sopenharmony_ci            buffer);
405519ea8026Sopenharmony_ci    if (tag < 0) {
405619ea8026Sopenharmony_ci        if (tag == LFS_ERR_NOENT) {
405719ea8026Sopenharmony_ci            return LFS_ERR_NOATTR;
405819ea8026Sopenharmony_ci        }
405919ea8026Sopenharmony_ci
406019ea8026Sopenharmony_ci        return tag;
406119ea8026Sopenharmony_ci    }
406219ea8026Sopenharmony_ci
406319ea8026Sopenharmony_ci    return lfs_tag_size(tag);
406419ea8026Sopenharmony_ci}
406519ea8026Sopenharmony_ci
406619ea8026Sopenharmony_ci#ifndef LFS_READONLY
406719ea8026Sopenharmony_cistatic int lfs_commitattr(lfs_t *lfs, const char *path,
406819ea8026Sopenharmony_ci        uint8_t type, const void *buffer, lfs_size_t size) {
406919ea8026Sopenharmony_ci    lfs_mdir_t cwd;
407019ea8026Sopenharmony_ci    lfs_stag_t tag = lfs_dir_find(lfs, &cwd, &path, NULL);
407119ea8026Sopenharmony_ci    if (tag < 0) {
407219ea8026Sopenharmony_ci        return tag;
407319ea8026Sopenharmony_ci    }
407419ea8026Sopenharmony_ci
407519ea8026Sopenharmony_ci    uint16_t id = lfs_tag_id(tag);
407619ea8026Sopenharmony_ci    if (id == 0x3ff) {
407719ea8026Sopenharmony_ci        // special case for root
407819ea8026Sopenharmony_ci        id = 0;
407919ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, &cwd, lfs->root);
408019ea8026Sopenharmony_ci        if (err) {
408119ea8026Sopenharmony_ci            return err;
408219ea8026Sopenharmony_ci        }
408319ea8026Sopenharmony_ci    }
408419ea8026Sopenharmony_ci
408519ea8026Sopenharmony_ci    return lfs_dir_commit(lfs, &cwd, LFS_MKATTRS(
408619ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_USERATTR + type, id, size), buffer}));
408719ea8026Sopenharmony_ci}
408819ea8026Sopenharmony_ci#endif
408919ea8026Sopenharmony_ci
409019ea8026Sopenharmony_ci#ifndef LFS_READONLY
409119ea8026Sopenharmony_cistatic int lfs_rawsetattr(lfs_t *lfs, const char *path,
409219ea8026Sopenharmony_ci        uint8_t type, const void *buffer, lfs_size_t size) {
409319ea8026Sopenharmony_ci    if (size > lfs->attr_max) {
409419ea8026Sopenharmony_ci        return LFS_ERR_NOSPC;
409519ea8026Sopenharmony_ci    }
409619ea8026Sopenharmony_ci
409719ea8026Sopenharmony_ci    return lfs_commitattr(lfs, path, type, buffer, size);
409819ea8026Sopenharmony_ci}
409919ea8026Sopenharmony_ci#endif
410019ea8026Sopenharmony_ci
410119ea8026Sopenharmony_ci#ifndef LFS_READONLY
410219ea8026Sopenharmony_cistatic int lfs_rawremoveattr(lfs_t *lfs, const char *path, uint8_t type) {
410319ea8026Sopenharmony_ci    return lfs_commitattr(lfs, path, type, NULL, 0x3ff);
410419ea8026Sopenharmony_ci}
410519ea8026Sopenharmony_ci#endif
410619ea8026Sopenharmony_ci
410719ea8026Sopenharmony_ci
410819ea8026Sopenharmony_ci/// Filesystem operations ///
410919ea8026Sopenharmony_cistatic int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
411019ea8026Sopenharmony_ci    lfs->cfg = cfg;
411119ea8026Sopenharmony_ci    lfs->block_count = cfg->block_count;  // May be 0
411219ea8026Sopenharmony_ci    int err = 0;
411319ea8026Sopenharmony_ci
411419ea8026Sopenharmony_ci#ifdef LFS_MULTIVERSION
411519ea8026Sopenharmony_ci    // this driver only supports minor version < current minor version
411619ea8026Sopenharmony_ci    LFS_ASSERT(!lfs->cfg->disk_version || (
411719ea8026Sopenharmony_ci            (0xffff & (lfs->cfg->disk_version >> 16))
411819ea8026Sopenharmony_ci                    == LFS_DISK_VERSION_MAJOR
411919ea8026Sopenharmony_ci                && (0xffff & (lfs->cfg->disk_version >> 0))
412019ea8026Sopenharmony_ci                    <= LFS_DISK_VERSION_MINOR));
412119ea8026Sopenharmony_ci#endif
412219ea8026Sopenharmony_ci
412319ea8026Sopenharmony_ci    // check that bool is a truthy-preserving type
412419ea8026Sopenharmony_ci    //
412519ea8026Sopenharmony_ci    // note the most common reason for this failure is a before-c99 compiler,
412619ea8026Sopenharmony_ci    // which littlefs currently does not support
412719ea8026Sopenharmony_ci    LFS_ASSERT((bool)0x80000000);
412819ea8026Sopenharmony_ci
412919ea8026Sopenharmony_ci    // validate that the lfs-cfg sizes were initiated properly before
413019ea8026Sopenharmony_ci    // performing any arithmetic logics with them
413119ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->read_size != 0);
413219ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->prog_size != 0);
413319ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->cache_size != 0);
413419ea8026Sopenharmony_ci
413519ea8026Sopenharmony_ci    // check that block size is a multiple of cache size is a multiple
413619ea8026Sopenharmony_ci    // of prog and read sizes
413719ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->read_size == 0);
413819ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->cache_size % lfs->cfg->prog_size == 0);
413919ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->cache_size == 0);
414019ea8026Sopenharmony_ci
414119ea8026Sopenharmony_ci    // check that the block size is large enough to fit all ctz pointers
414219ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->block_size >= 128);
414319ea8026Sopenharmony_ci    // this is the exact calculation for all ctz pointers, if this fails
414419ea8026Sopenharmony_ci    // and the simpler assert above does not, math must be broken
414519ea8026Sopenharmony_ci    LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
414619ea8026Sopenharmony_ci            <= lfs->cfg->block_size);
414719ea8026Sopenharmony_ci
414819ea8026Sopenharmony_ci    // block_cycles = 0 is no longer supported.
414919ea8026Sopenharmony_ci    //
415019ea8026Sopenharmony_ci    // block_cycles is the number of erase cycles before littlefs evicts
415119ea8026Sopenharmony_ci    // metadata logs as a part of wear leveling. Suggested values are in the
415219ea8026Sopenharmony_ci    // range of 100-1000, or set block_cycles to -1 to disable block-level
415319ea8026Sopenharmony_ci    // wear-leveling.
415419ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->block_cycles != 0);
415519ea8026Sopenharmony_ci
415619ea8026Sopenharmony_ci
415719ea8026Sopenharmony_ci    // setup read cache
415819ea8026Sopenharmony_ci    if (lfs->cfg->read_buffer) {
415919ea8026Sopenharmony_ci        lfs->rcache.buffer = lfs->cfg->read_buffer;
416019ea8026Sopenharmony_ci    } else {
416119ea8026Sopenharmony_ci        lfs->rcache.buffer = lfs_malloc(lfs->cfg->cache_size);
416219ea8026Sopenharmony_ci        if (!lfs->rcache.buffer) {
416319ea8026Sopenharmony_ci            err = LFS_ERR_NOMEM;
416419ea8026Sopenharmony_ci            goto cleanup;
416519ea8026Sopenharmony_ci        }
416619ea8026Sopenharmony_ci    }
416719ea8026Sopenharmony_ci
416819ea8026Sopenharmony_ci    // setup program cache
416919ea8026Sopenharmony_ci    if (lfs->cfg->prog_buffer) {
417019ea8026Sopenharmony_ci        lfs->pcache.buffer = lfs->cfg->prog_buffer;
417119ea8026Sopenharmony_ci    } else {
417219ea8026Sopenharmony_ci        lfs->pcache.buffer = lfs_malloc(lfs->cfg->cache_size);
417319ea8026Sopenharmony_ci        if (!lfs->pcache.buffer) {
417419ea8026Sopenharmony_ci            err = LFS_ERR_NOMEM;
417519ea8026Sopenharmony_ci            goto cleanup;
417619ea8026Sopenharmony_ci        }
417719ea8026Sopenharmony_ci    }
417819ea8026Sopenharmony_ci
417919ea8026Sopenharmony_ci    // zero to avoid information leaks
418019ea8026Sopenharmony_ci    lfs_cache_zero(lfs, &lfs->rcache);
418119ea8026Sopenharmony_ci    lfs_cache_zero(lfs, &lfs->pcache);
418219ea8026Sopenharmony_ci
418319ea8026Sopenharmony_ci    // setup lookahead, must be multiple of 64-bits, 32-bit aligned
418419ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->lookahead_size > 0);
418519ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->lookahead_size % 8 == 0 &&
418619ea8026Sopenharmony_ci            (uintptr_t)lfs->cfg->lookahead_buffer % 4 == 0);
418719ea8026Sopenharmony_ci    if (lfs->cfg->lookahead_buffer) {
418819ea8026Sopenharmony_ci        lfs->free.buffer = lfs->cfg->lookahead_buffer;
418919ea8026Sopenharmony_ci    } else {
419019ea8026Sopenharmony_ci        lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead_size);
419119ea8026Sopenharmony_ci        if (!lfs->free.buffer) {
419219ea8026Sopenharmony_ci            err = LFS_ERR_NOMEM;
419319ea8026Sopenharmony_ci            goto cleanup;
419419ea8026Sopenharmony_ci        }
419519ea8026Sopenharmony_ci    }
419619ea8026Sopenharmony_ci
419719ea8026Sopenharmony_ci    // check that the size limits are sane
419819ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->name_max <= LFS_NAME_MAX);
419919ea8026Sopenharmony_ci    lfs->name_max = lfs->cfg->name_max;
420019ea8026Sopenharmony_ci    if (!lfs->name_max) {
420119ea8026Sopenharmony_ci        lfs->name_max = LFS_NAME_MAX;
420219ea8026Sopenharmony_ci    }
420319ea8026Sopenharmony_ci
420419ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->file_max <= LFS_FILE_MAX);
420519ea8026Sopenharmony_ci    lfs->file_max = lfs->cfg->file_max;
420619ea8026Sopenharmony_ci    if (!lfs->file_max) {
420719ea8026Sopenharmony_ci        lfs->file_max = LFS_FILE_MAX;
420819ea8026Sopenharmony_ci    }
420919ea8026Sopenharmony_ci
421019ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->attr_max <= LFS_ATTR_MAX);
421119ea8026Sopenharmony_ci    lfs->attr_max = lfs->cfg->attr_max;
421219ea8026Sopenharmony_ci    if (!lfs->attr_max) {
421319ea8026Sopenharmony_ci        lfs->attr_max = LFS_ATTR_MAX;
421419ea8026Sopenharmony_ci    }
421519ea8026Sopenharmony_ci
421619ea8026Sopenharmony_ci    LFS_ASSERT(lfs->cfg->metadata_max <= lfs->cfg->block_size);
421719ea8026Sopenharmony_ci
421819ea8026Sopenharmony_ci    // setup default state
421919ea8026Sopenharmony_ci    lfs->root[0] = LFS_BLOCK_NULL;
422019ea8026Sopenharmony_ci    lfs->root[1] = LFS_BLOCK_NULL;
422119ea8026Sopenharmony_ci    lfs->mlist = NULL;
422219ea8026Sopenharmony_ci    lfs->seed = 0;
422319ea8026Sopenharmony_ci    lfs->gdisk = (lfs_gstate_t){0};
422419ea8026Sopenharmony_ci    lfs->gstate = (lfs_gstate_t){0};
422519ea8026Sopenharmony_ci    lfs->gdelta = (lfs_gstate_t){0};
422619ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
422719ea8026Sopenharmony_ci    lfs->lfs1 = NULL;
422819ea8026Sopenharmony_ci#endif
422919ea8026Sopenharmony_ci
423019ea8026Sopenharmony_ci    return 0;
423119ea8026Sopenharmony_ci
423219ea8026Sopenharmony_cicleanup:
423319ea8026Sopenharmony_ci    lfs_deinit(lfs);
423419ea8026Sopenharmony_ci    return err;
423519ea8026Sopenharmony_ci}
423619ea8026Sopenharmony_ci
423719ea8026Sopenharmony_cistatic int lfs_deinit(lfs_t *lfs) {
423819ea8026Sopenharmony_ci    // free allocated memory
423919ea8026Sopenharmony_ci    if (!lfs->cfg->read_buffer) {
424019ea8026Sopenharmony_ci        lfs_free(lfs->rcache.buffer);
424119ea8026Sopenharmony_ci    }
424219ea8026Sopenharmony_ci
424319ea8026Sopenharmony_ci    if (!lfs->cfg->prog_buffer) {
424419ea8026Sopenharmony_ci        lfs_free(lfs->pcache.buffer);
424519ea8026Sopenharmony_ci    }
424619ea8026Sopenharmony_ci
424719ea8026Sopenharmony_ci    if (!lfs->cfg->lookahead_buffer) {
424819ea8026Sopenharmony_ci        lfs_free(lfs->free.buffer);
424919ea8026Sopenharmony_ci    }
425019ea8026Sopenharmony_ci
425119ea8026Sopenharmony_ci    return 0;
425219ea8026Sopenharmony_ci}
425319ea8026Sopenharmony_ci
425419ea8026Sopenharmony_ci
425519ea8026Sopenharmony_ci
425619ea8026Sopenharmony_ci#ifndef LFS_READONLY
425719ea8026Sopenharmony_cistatic int lfs_rawformat(lfs_t *lfs, const struct lfs_config *cfg) {
425819ea8026Sopenharmony_ci    int err = 0;
425919ea8026Sopenharmony_ci    {
426019ea8026Sopenharmony_ci        err = lfs_init(lfs, cfg);
426119ea8026Sopenharmony_ci        if (err) {
426219ea8026Sopenharmony_ci            return err;
426319ea8026Sopenharmony_ci        }
426419ea8026Sopenharmony_ci
426519ea8026Sopenharmony_ci        LFS_ASSERT(cfg->block_count != 0);
426619ea8026Sopenharmony_ci
426719ea8026Sopenharmony_ci        // create free lookahead
426819ea8026Sopenharmony_ci        memset(lfs->free.buffer, 0, lfs->cfg->lookahead_size);
426919ea8026Sopenharmony_ci        lfs->free.off = 0;
427019ea8026Sopenharmony_ci        lfs->free.size = lfs_min(8*lfs->cfg->lookahead_size,
427119ea8026Sopenharmony_ci                lfs->block_count);
427219ea8026Sopenharmony_ci        lfs->free.i = 0;
427319ea8026Sopenharmony_ci        lfs_alloc_ack(lfs);
427419ea8026Sopenharmony_ci
427519ea8026Sopenharmony_ci        // create root dir
427619ea8026Sopenharmony_ci        lfs_mdir_t root;
427719ea8026Sopenharmony_ci        err = lfs_dir_alloc(lfs, &root);
427819ea8026Sopenharmony_ci        if (err) {
427919ea8026Sopenharmony_ci            goto cleanup;
428019ea8026Sopenharmony_ci        }
428119ea8026Sopenharmony_ci
428219ea8026Sopenharmony_ci        // write one superblock
428319ea8026Sopenharmony_ci        lfs_superblock_t superblock = {
428419ea8026Sopenharmony_ci            .version     = lfs_fs_disk_version(lfs),
428519ea8026Sopenharmony_ci            .block_size  = lfs->cfg->block_size,
428619ea8026Sopenharmony_ci            .block_count = lfs->block_count,
428719ea8026Sopenharmony_ci            .name_max    = lfs->name_max,
428819ea8026Sopenharmony_ci            .file_max    = lfs->file_max,
428919ea8026Sopenharmony_ci            .attr_max    = lfs->attr_max,
429019ea8026Sopenharmony_ci        };
429119ea8026Sopenharmony_ci
429219ea8026Sopenharmony_ci        lfs_superblock_tole32(&superblock);
429319ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
429419ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
429519ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
429619ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
429719ea8026Sopenharmony_ci                    &superblock}));
429819ea8026Sopenharmony_ci        if (err) {
429919ea8026Sopenharmony_ci            goto cleanup;
430019ea8026Sopenharmony_ci        }
430119ea8026Sopenharmony_ci
430219ea8026Sopenharmony_ci        // force compaction to prevent accidentally mounting any
430319ea8026Sopenharmony_ci        // older version of littlefs that may live on disk
430419ea8026Sopenharmony_ci        root.erased = false;
430519ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &root, NULL, 0);
430619ea8026Sopenharmony_ci        if (err) {
430719ea8026Sopenharmony_ci            goto cleanup;
430819ea8026Sopenharmony_ci        }
430919ea8026Sopenharmony_ci
431019ea8026Sopenharmony_ci        // sanity check that fetch works
431119ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1});
431219ea8026Sopenharmony_ci        if (err) {
431319ea8026Sopenharmony_ci            goto cleanup;
431419ea8026Sopenharmony_ci        }
431519ea8026Sopenharmony_ci    }
431619ea8026Sopenharmony_ci
431719ea8026Sopenharmony_cicleanup:
431819ea8026Sopenharmony_ci    lfs_deinit(lfs);
431919ea8026Sopenharmony_ci    return err;
432019ea8026Sopenharmony_ci
432119ea8026Sopenharmony_ci}
432219ea8026Sopenharmony_ci#endif
432319ea8026Sopenharmony_ci
432419ea8026Sopenharmony_cistatic int lfs_rawmount(lfs_t *lfs, const struct lfs_config *cfg) {
432519ea8026Sopenharmony_ci    int err = lfs_init(lfs, cfg);
432619ea8026Sopenharmony_ci    if (err) {
432719ea8026Sopenharmony_ci        return err;
432819ea8026Sopenharmony_ci    }
432919ea8026Sopenharmony_ci
433019ea8026Sopenharmony_ci    // scan directory blocks for superblock and any global updates
433119ea8026Sopenharmony_ci    lfs_mdir_t dir = {.tail = {0, 1}};
433219ea8026Sopenharmony_ci    lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
433319ea8026Sopenharmony_ci    lfs_size_t tortoise_i = 1;
433419ea8026Sopenharmony_ci    lfs_size_t tortoise_period = 1;
433519ea8026Sopenharmony_ci    while (!lfs_pair_isnull(dir.tail)) {
433619ea8026Sopenharmony_ci        // detect cycles with Brent's algorithm
433719ea8026Sopenharmony_ci        if (lfs_pair_issync(dir.tail, tortoise)) {
433819ea8026Sopenharmony_ci            LFS_WARN("Cycle detected in tail list");
433919ea8026Sopenharmony_ci            err = LFS_ERR_CORRUPT;
434019ea8026Sopenharmony_ci            goto cleanup;
434119ea8026Sopenharmony_ci        }
434219ea8026Sopenharmony_ci        if (tortoise_i == tortoise_period) {
434319ea8026Sopenharmony_ci            tortoise[0] = dir.tail[0];
434419ea8026Sopenharmony_ci            tortoise[1] = dir.tail[1];
434519ea8026Sopenharmony_ci            tortoise_i = 0;
434619ea8026Sopenharmony_ci            tortoise_period *= 2;
434719ea8026Sopenharmony_ci        }
434819ea8026Sopenharmony_ci        tortoise_i += 1;
434919ea8026Sopenharmony_ci
435019ea8026Sopenharmony_ci        // fetch next block in tail list
435119ea8026Sopenharmony_ci        lfs_stag_t tag = lfs_dir_fetchmatch(lfs, &dir, dir.tail,
435219ea8026Sopenharmony_ci                LFS_MKTAG(0x7ff, 0x3ff, 0),
435319ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8),
435419ea8026Sopenharmony_ci                NULL,
435519ea8026Sopenharmony_ci                lfs_dir_find_match, &(struct lfs_dir_find_match){
435619ea8026Sopenharmony_ci                    lfs, "littlefs", 8});
435719ea8026Sopenharmony_ci        if (tag < 0) {
435819ea8026Sopenharmony_ci            err = tag;
435919ea8026Sopenharmony_ci            goto cleanup;
436019ea8026Sopenharmony_ci        }
436119ea8026Sopenharmony_ci
436219ea8026Sopenharmony_ci        // has superblock?
436319ea8026Sopenharmony_ci        if (tag && !lfs_tag_isdelete(tag)) {
436419ea8026Sopenharmony_ci            // update root
436519ea8026Sopenharmony_ci            lfs->root[0] = dir.pair[0];
436619ea8026Sopenharmony_ci            lfs->root[1] = dir.pair[1];
436719ea8026Sopenharmony_ci
436819ea8026Sopenharmony_ci            // grab superblock
436919ea8026Sopenharmony_ci            lfs_superblock_t superblock;
437019ea8026Sopenharmony_ci            tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0),
437119ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
437219ea8026Sopenharmony_ci                    &superblock);
437319ea8026Sopenharmony_ci            if (tag < 0) {
437419ea8026Sopenharmony_ci                err = tag;
437519ea8026Sopenharmony_ci                goto cleanup;
437619ea8026Sopenharmony_ci            }
437719ea8026Sopenharmony_ci            lfs_superblock_fromle32(&superblock);
437819ea8026Sopenharmony_ci
437919ea8026Sopenharmony_ci            // check version
438019ea8026Sopenharmony_ci            uint16_t major_version = (0xffff & (superblock.version >> 16));
438119ea8026Sopenharmony_ci            uint16_t minor_version = (0xffff & (superblock.version >>  0));
438219ea8026Sopenharmony_ci            if (major_version != lfs_fs_disk_version_major(lfs)
438319ea8026Sopenharmony_ci                    || minor_version > lfs_fs_disk_version_minor(lfs)) {
438419ea8026Sopenharmony_ci                LFS_ERROR("Invalid version "
438519ea8026Sopenharmony_ci                        "v%"PRIu16".%"PRIu16" != v%"PRIu16".%"PRIu16,
438619ea8026Sopenharmony_ci                        major_version,
438719ea8026Sopenharmony_ci                        minor_version,
438819ea8026Sopenharmony_ci                        lfs_fs_disk_version_major(lfs),
438919ea8026Sopenharmony_ci                        lfs_fs_disk_version_minor(lfs));
439019ea8026Sopenharmony_ci                err = LFS_ERR_INVAL;
439119ea8026Sopenharmony_ci                goto cleanup;
439219ea8026Sopenharmony_ci            }
439319ea8026Sopenharmony_ci
439419ea8026Sopenharmony_ci            // found older minor version? set an in-device only bit in the
439519ea8026Sopenharmony_ci            // gstate so we know we need to rewrite the superblock before
439619ea8026Sopenharmony_ci            // the first write
439719ea8026Sopenharmony_ci            if (minor_version < lfs_fs_disk_version_minor(lfs)) {
439819ea8026Sopenharmony_ci                LFS_DEBUG("Found older minor version "
439919ea8026Sopenharmony_ci                        "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16,
440019ea8026Sopenharmony_ci                        major_version,
440119ea8026Sopenharmony_ci                        minor_version,
440219ea8026Sopenharmony_ci                        lfs_fs_disk_version_major(lfs),
440319ea8026Sopenharmony_ci                        lfs_fs_disk_version_minor(lfs));
440419ea8026Sopenharmony_ci                // note this bit is reserved on disk, so fetching more gstate
440519ea8026Sopenharmony_ci                // will not interfere here
440619ea8026Sopenharmony_ci                lfs_fs_prepsuperblock(lfs, true);
440719ea8026Sopenharmony_ci            }
440819ea8026Sopenharmony_ci
440919ea8026Sopenharmony_ci            // check superblock configuration
441019ea8026Sopenharmony_ci            if (superblock.name_max) {
441119ea8026Sopenharmony_ci                if (superblock.name_max > lfs->name_max) {
441219ea8026Sopenharmony_ci                    LFS_ERROR("Unsupported name_max (%"PRIu32" > %"PRIu32")",
441319ea8026Sopenharmony_ci                            superblock.name_max, lfs->name_max);
441419ea8026Sopenharmony_ci                    err = LFS_ERR_INVAL;
441519ea8026Sopenharmony_ci                    goto cleanup;
441619ea8026Sopenharmony_ci                }
441719ea8026Sopenharmony_ci
441819ea8026Sopenharmony_ci                lfs->name_max = superblock.name_max;
441919ea8026Sopenharmony_ci            }
442019ea8026Sopenharmony_ci
442119ea8026Sopenharmony_ci            if (superblock.file_max) {
442219ea8026Sopenharmony_ci                if (superblock.file_max > lfs->file_max) {
442319ea8026Sopenharmony_ci                    LFS_ERROR("Unsupported file_max (%"PRIu32" > %"PRIu32")",
442419ea8026Sopenharmony_ci                            superblock.file_max, lfs->file_max);
442519ea8026Sopenharmony_ci                    err = LFS_ERR_INVAL;
442619ea8026Sopenharmony_ci                    goto cleanup;
442719ea8026Sopenharmony_ci                }
442819ea8026Sopenharmony_ci
442919ea8026Sopenharmony_ci                lfs->file_max = superblock.file_max;
443019ea8026Sopenharmony_ci            }
443119ea8026Sopenharmony_ci
443219ea8026Sopenharmony_ci            if (superblock.attr_max) {
443319ea8026Sopenharmony_ci                if (superblock.attr_max > lfs->attr_max) {
443419ea8026Sopenharmony_ci                    LFS_ERROR("Unsupported attr_max (%"PRIu32" > %"PRIu32")",
443519ea8026Sopenharmony_ci                            superblock.attr_max, lfs->attr_max);
443619ea8026Sopenharmony_ci                    err = LFS_ERR_INVAL;
443719ea8026Sopenharmony_ci                    goto cleanup;
443819ea8026Sopenharmony_ci                }
443919ea8026Sopenharmony_ci
444019ea8026Sopenharmony_ci                lfs->attr_max = superblock.attr_max;
444119ea8026Sopenharmony_ci            }
444219ea8026Sopenharmony_ci
444319ea8026Sopenharmony_ci            // this is where we get the block_count from disk if block_count=0
444419ea8026Sopenharmony_ci            if (lfs->cfg->block_count
444519ea8026Sopenharmony_ci                    && superblock.block_count != lfs->cfg->block_count) {
444619ea8026Sopenharmony_ci                LFS_ERROR("Invalid block count (%"PRIu32" != %"PRIu32")",
444719ea8026Sopenharmony_ci                        superblock.block_count, lfs->cfg->block_count);
444819ea8026Sopenharmony_ci                err = LFS_ERR_INVAL;
444919ea8026Sopenharmony_ci                goto cleanup;
445019ea8026Sopenharmony_ci            }
445119ea8026Sopenharmony_ci
445219ea8026Sopenharmony_ci            lfs->block_count = superblock.block_count;
445319ea8026Sopenharmony_ci
445419ea8026Sopenharmony_ci            if (superblock.block_size != lfs->cfg->block_size) {
445519ea8026Sopenharmony_ci                LFS_ERROR("Invalid block size (%"PRIu32" != %"PRIu32")",
445619ea8026Sopenharmony_ci                        superblock.block_size, lfs->cfg->block_size);
445719ea8026Sopenharmony_ci                err = LFS_ERR_INVAL;
445819ea8026Sopenharmony_ci                goto cleanup;
445919ea8026Sopenharmony_ci            }
446019ea8026Sopenharmony_ci        }
446119ea8026Sopenharmony_ci
446219ea8026Sopenharmony_ci        // has gstate?
446319ea8026Sopenharmony_ci        err = lfs_dir_getgstate(lfs, &dir, &lfs->gstate);
446419ea8026Sopenharmony_ci        if (err) {
446519ea8026Sopenharmony_ci            goto cleanup;
446619ea8026Sopenharmony_ci        }
446719ea8026Sopenharmony_ci    }
446819ea8026Sopenharmony_ci
446919ea8026Sopenharmony_ci    // update littlefs with gstate
447019ea8026Sopenharmony_ci    if (!lfs_gstate_iszero(&lfs->gstate)) {
447119ea8026Sopenharmony_ci        LFS_DEBUG("Found pending gstate 0x%08"PRIx32"%08"PRIx32"%08"PRIx32,
447219ea8026Sopenharmony_ci                lfs->gstate.tag,
447319ea8026Sopenharmony_ci                lfs->gstate.pair[0],
447419ea8026Sopenharmony_ci                lfs->gstate.pair[1]);
447519ea8026Sopenharmony_ci    }
447619ea8026Sopenharmony_ci    lfs->gstate.tag += !lfs_tag_isvalid(lfs->gstate.tag);
447719ea8026Sopenharmony_ci    lfs->gdisk = lfs->gstate;
447819ea8026Sopenharmony_ci
447919ea8026Sopenharmony_ci    // setup free lookahead, to distribute allocations uniformly across
448019ea8026Sopenharmony_ci    // boots, we start the allocator at a random location
448119ea8026Sopenharmony_ci    lfs->free.off = lfs->seed % lfs->block_count;
448219ea8026Sopenharmony_ci    lfs_alloc_drop(lfs);
448319ea8026Sopenharmony_ci
448419ea8026Sopenharmony_ci    return 0;
448519ea8026Sopenharmony_ci
448619ea8026Sopenharmony_cicleanup:
448719ea8026Sopenharmony_ci    lfs_rawunmount(lfs);
448819ea8026Sopenharmony_ci    return err;
448919ea8026Sopenharmony_ci}
449019ea8026Sopenharmony_ci
449119ea8026Sopenharmony_cistatic int lfs_rawunmount(lfs_t *lfs) {
449219ea8026Sopenharmony_ci    return lfs_deinit(lfs);
449319ea8026Sopenharmony_ci}
449419ea8026Sopenharmony_ci
449519ea8026Sopenharmony_ci
449619ea8026Sopenharmony_ci/// Filesystem filesystem operations ///
449719ea8026Sopenharmony_cistatic int lfs_fs_rawstat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
449819ea8026Sopenharmony_ci    // if the superblock is up-to-date, we must be on the most recent
449919ea8026Sopenharmony_ci    // minor version of littlefs
450019ea8026Sopenharmony_ci    if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
450119ea8026Sopenharmony_ci        fsinfo->disk_version = lfs_fs_disk_version(lfs);
450219ea8026Sopenharmony_ci
450319ea8026Sopenharmony_ci    // otherwise we need to read the minor version on disk
450419ea8026Sopenharmony_ci    } else {
450519ea8026Sopenharmony_ci        // fetch the superblock
450619ea8026Sopenharmony_ci        lfs_mdir_t dir;
450719ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, &dir, lfs->root);
450819ea8026Sopenharmony_ci        if (err) {
450919ea8026Sopenharmony_ci            return err;
451019ea8026Sopenharmony_ci        }
451119ea8026Sopenharmony_ci
451219ea8026Sopenharmony_ci        lfs_superblock_t superblock;
451319ea8026Sopenharmony_ci        lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x7ff, 0x3ff, 0),
451419ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
451519ea8026Sopenharmony_ci                &superblock);
451619ea8026Sopenharmony_ci        if (tag < 0) {
451719ea8026Sopenharmony_ci            return tag;
451819ea8026Sopenharmony_ci        }
451919ea8026Sopenharmony_ci        lfs_superblock_fromle32(&superblock);
452019ea8026Sopenharmony_ci
452119ea8026Sopenharmony_ci        // read the on-disk version
452219ea8026Sopenharmony_ci        fsinfo->disk_version = superblock.version;
452319ea8026Sopenharmony_ci    }
452419ea8026Sopenharmony_ci
452519ea8026Sopenharmony_ci    // filesystem geometry
452619ea8026Sopenharmony_ci    fsinfo->block_size = lfs->cfg->block_size;
452719ea8026Sopenharmony_ci    fsinfo->block_count = lfs->block_count;
452819ea8026Sopenharmony_ci
452919ea8026Sopenharmony_ci    // other on-disk configuration, we cache all of these for internal use
453019ea8026Sopenharmony_ci    fsinfo->name_max = lfs->name_max;
453119ea8026Sopenharmony_ci    fsinfo->file_max = lfs->file_max;
453219ea8026Sopenharmony_ci    fsinfo->attr_max = lfs->attr_max;
453319ea8026Sopenharmony_ci
453419ea8026Sopenharmony_ci    return 0;
453519ea8026Sopenharmony_ci}
453619ea8026Sopenharmony_ci
453719ea8026Sopenharmony_ciint lfs_fs_rawtraverse(lfs_t *lfs,
453819ea8026Sopenharmony_ci        int (*cb)(void *data, lfs_block_t block), void *data,
453919ea8026Sopenharmony_ci        bool includeorphans) {
454019ea8026Sopenharmony_ci    // iterate over metadata pairs
454119ea8026Sopenharmony_ci    lfs_mdir_t dir = {.tail = {0, 1}};
454219ea8026Sopenharmony_ci
454319ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
454419ea8026Sopenharmony_ci    // also consider v1 blocks during migration
454519ea8026Sopenharmony_ci    if (lfs->lfs1) {
454619ea8026Sopenharmony_ci        int err = lfs1_traverse(lfs, cb, data);
454719ea8026Sopenharmony_ci        if (err) {
454819ea8026Sopenharmony_ci            return err;
454919ea8026Sopenharmony_ci        }
455019ea8026Sopenharmony_ci
455119ea8026Sopenharmony_ci        dir.tail[0] = lfs->root[0];
455219ea8026Sopenharmony_ci        dir.tail[1] = lfs->root[1];
455319ea8026Sopenharmony_ci    }
455419ea8026Sopenharmony_ci#endif
455519ea8026Sopenharmony_ci
455619ea8026Sopenharmony_ci    lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
455719ea8026Sopenharmony_ci    lfs_size_t tortoise_i = 1;
455819ea8026Sopenharmony_ci    lfs_size_t tortoise_period = 1;
455919ea8026Sopenharmony_ci    while (!lfs_pair_isnull(dir.tail)) {
456019ea8026Sopenharmony_ci        // detect cycles with Brent's algorithm
456119ea8026Sopenharmony_ci        if (lfs_pair_issync(dir.tail, tortoise)) {
456219ea8026Sopenharmony_ci            LFS_WARN("Cycle detected in tail list");
456319ea8026Sopenharmony_ci            return LFS_ERR_CORRUPT;
456419ea8026Sopenharmony_ci        }
456519ea8026Sopenharmony_ci        if (tortoise_i == tortoise_period) {
456619ea8026Sopenharmony_ci            tortoise[0] = dir.tail[0];
456719ea8026Sopenharmony_ci            tortoise[1] = dir.tail[1];
456819ea8026Sopenharmony_ci            tortoise_i = 0;
456919ea8026Sopenharmony_ci            tortoise_period *= 2;
457019ea8026Sopenharmony_ci        }
457119ea8026Sopenharmony_ci        tortoise_i += 1;
457219ea8026Sopenharmony_ci
457319ea8026Sopenharmony_ci        for (int i = 0; i < 2; i++) {
457419ea8026Sopenharmony_ci            int err = cb(data, dir.tail[i]);
457519ea8026Sopenharmony_ci            if (err) {
457619ea8026Sopenharmony_ci                return err;
457719ea8026Sopenharmony_ci            }
457819ea8026Sopenharmony_ci        }
457919ea8026Sopenharmony_ci
458019ea8026Sopenharmony_ci        // iterate through ids in directory
458119ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, &dir, dir.tail);
458219ea8026Sopenharmony_ci        if (err) {
458319ea8026Sopenharmony_ci            return err;
458419ea8026Sopenharmony_ci        }
458519ea8026Sopenharmony_ci
458619ea8026Sopenharmony_ci        for (uint16_t id = 0; id < dir.count; id++) {
458719ea8026Sopenharmony_ci            struct lfs_ctz ctz;
458819ea8026Sopenharmony_ci            lfs_stag_t tag = lfs_dir_get(lfs, &dir, LFS_MKTAG(0x700, 0x3ff, 0),
458919ea8026Sopenharmony_ci                    LFS_MKTAG(LFS_TYPE_STRUCT, id, sizeof(ctz)), &ctz);
459019ea8026Sopenharmony_ci            if (tag < 0) {
459119ea8026Sopenharmony_ci                if (tag == LFS_ERR_NOENT) {
459219ea8026Sopenharmony_ci                    continue;
459319ea8026Sopenharmony_ci                }
459419ea8026Sopenharmony_ci                return tag;
459519ea8026Sopenharmony_ci            }
459619ea8026Sopenharmony_ci            lfs_ctz_fromle32(&ctz);
459719ea8026Sopenharmony_ci
459819ea8026Sopenharmony_ci            if (lfs_tag_type3(tag) == LFS_TYPE_CTZSTRUCT) {
459919ea8026Sopenharmony_ci                err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache,
460019ea8026Sopenharmony_ci                        ctz.head, ctz.size, cb, data);
460119ea8026Sopenharmony_ci                if (err) {
460219ea8026Sopenharmony_ci                    return err;
460319ea8026Sopenharmony_ci                }
460419ea8026Sopenharmony_ci            } else if (includeorphans &&
460519ea8026Sopenharmony_ci                    lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) {
460619ea8026Sopenharmony_ci                for (int i = 0; i < 2; i++) {
460719ea8026Sopenharmony_ci                    err = cb(data, (&ctz.head)[i]);
460819ea8026Sopenharmony_ci                    if (err) {
460919ea8026Sopenharmony_ci                        return err;
461019ea8026Sopenharmony_ci                    }
461119ea8026Sopenharmony_ci                }
461219ea8026Sopenharmony_ci            }
461319ea8026Sopenharmony_ci        }
461419ea8026Sopenharmony_ci    }
461519ea8026Sopenharmony_ci
461619ea8026Sopenharmony_ci#ifndef LFS_READONLY
461719ea8026Sopenharmony_ci    // iterate over any open files
461819ea8026Sopenharmony_ci    for (lfs_file_t *f = (lfs_file_t*)lfs->mlist; f; f = f->next) {
461919ea8026Sopenharmony_ci        if (f->type != LFS_TYPE_REG) {
462019ea8026Sopenharmony_ci            continue;
462119ea8026Sopenharmony_ci        }
462219ea8026Sopenharmony_ci
462319ea8026Sopenharmony_ci        if ((f->flags & LFS_F_DIRTY) && !(f->flags & LFS_F_INLINE)) {
462419ea8026Sopenharmony_ci            int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
462519ea8026Sopenharmony_ci                    f->ctz.head, f->ctz.size, cb, data);
462619ea8026Sopenharmony_ci            if (err) {
462719ea8026Sopenharmony_ci                return err;
462819ea8026Sopenharmony_ci            }
462919ea8026Sopenharmony_ci        }
463019ea8026Sopenharmony_ci
463119ea8026Sopenharmony_ci        if ((f->flags & LFS_F_WRITING) && !(f->flags & LFS_F_INLINE)) {
463219ea8026Sopenharmony_ci            int err = lfs_ctz_traverse(lfs, &f->cache, &lfs->rcache,
463319ea8026Sopenharmony_ci                    f->block, f->pos, cb, data);
463419ea8026Sopenharmony_ci            if (err) {
463519ea8026Sopenharmony_ci                return err;
463619ea8026Sopenharmony_ci            }
463719ea8026Sopenharmony_ci        }
463819ea8026Sopenharmony_ci    }
463919ea8026Sopenharmony_ci#endif
464019ea8026Sopenharmony_ci
464119ea8026Sopenharmony_ci    return 0;
464219ea8026Sopenharmony_ci}
464319ea8026Sopenharmony_ci
464419ea8026Sopenharmony_ci#ifndef LFS_READONLY
464519ea8026Sopenharmony_cistatic int lfs_fs_pred(lfs_t *lfs,
464619ea8026Sopenharmony_ci        const lfs_block_t pair[2], lfs_mdir_t *pdir) {
464719ea8026Sopenharmony_ci    // iterate over all directory directory entries
464819ea8026Sopenharmony_ci    pdir->tail[0] = 0;
464919ea8026Sopenharmony_ci    pdir->tail[1] = 1;
465019ea8026Sopenharmony_ci    lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
465119ea8026Sopenharmony_ci    lfs_size_t tortoise_i = 1;
465219ea8026Sopenharmony_ci    lfs_size_t tortoise_period = 1;
465319ea8026Sopenharmony_ci    while (!lfs_pair_isnull(pdir->tail)) {
465419ea8026Sopenharmony_ci        // detect cycles with Brent's algorithm
465519ea8026Sopenharmony_ci        if (lfs_pair_issync(pdir->tail, tortoise)) {
465619ea8026Sopenharmony_ci            LFS_WARN("Cycle detected in tail list");
465719ea8026Sopenharmony_ci            return LFS_ERR_CORRUPT;
465819ea8026Sopenharmony_ci        }
465919ea8026Sopenharmony_ci        if (tortoise_i == tortoise_period) {
466019ea8026Sopenharmony_ci            tortoise[0] = pdir->tail[0];
466119ea8026Sopenharmony_ci            tortoise[1] = pdir->tail[1];
466219ea8026Sopenharmony_ci            tortoise_i = 0;
466319ea8026Sopenharmony_ci            tortoise_period *= 2;
466419ea8026Sopenharmony_ci        }
466519ea8026Sopenharmony_ci        tortoise_i += 1;
466619ea8026Sopenharmony_ci
466719ea8026Sopenharmony_ci        if (lfs_pair_cmp(pdir->tail, pair) == 0) {
466819ea8026Sopenharmony_ci            return 0;
466919ea8026Sopenharmony_ci        }
467019ea8026Sopenharmony_ci
467119ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, pdir, pdir->tail);
467219ea8026Sopenharmony_ci        if (err) {
467319ea8026Sopenharmony_ci            return err;
467419ea8026Sopenharmony_ci        }
467519ea8026Sopenharmony_ci    }
467619ea8026Sopenharmony_ci
467719ea8026Sopenharmony_ci    return LFS_ERR_NOENT;
467819ea8026Sopenharmony_ci}
467919ea8026Sopenharmony_ci#endif
468019ea8026Sopenharmony_ci
468119ea8026Sopenharmony_ci#ifndef LFS_READONLY
468219ea8026Sopenharmony_cistruct lfs_fs_parent_match {
468319ea8026Sopenharmony_ci    lfs_t *lfs;
468419ea8026Sopenharmony_ci    const lfs_block_t pair[2];
468519ea8026Sopenharmony_ci};
468619ea8026Sopenharmony_ci#endif
468719ea8026Sopenharmony_ci
468819ea8026Sopenharmony_ci#ifndef LFS_READONLY
468919ea8026Sopenharmony_cistatic int lfs_fs_parent_match(void *data,
469019ea8026Sopenharmony_ci        lfs_tag_t tag, const void *buffer) {
469119ea8026Sopenharmony_ci    struct lfs_fs_parent_match *find = data;
469219ea8026Sopenharmony_ci    lfs_t *lfs = find->lfs;
469319ea8026Sopenharmony_ci    const struct lfs_diskoff *disk = buffer;
469419ea8026Sopenharmony_ci    (void)tag;
469519ea8026Sopenharmony_ci
469619ea8026Sopenharmony_ci    lfs_block_t child[2];
469719ea8026Sopenharmony_ci    int err = lfs_bd_read(lfs,
469819ea8026Sopenharmony_ci            &lfs->pcache, &lfs->rcache, lfs->cfg->block_size,
469919ea8026Sopenharmony_ci            disk->block, disk->off, &child, sizeof(child));
470019ea8026Sopenharmony_ci    if (err) {
470119ea8026Sopenharmony_ci        return err;
470219ea8026Sopenharmony_ci    }
470319ea8026Sopenharmony_ci
470419ea8026Sopenharmony_ci    lfs_pair_fromle32(child);
470519ea8026Sopenharmony_ci    return (lfs_pair_cmp(child, find->pair) == 0) ? LFS_CMP_EQ : LFS_CMP_LT;
470619ea8026Sopenharmony_ci}
470719ea8026Sopenharmony_ci#endif
470819ea8026Sopenharmony_ci
470919ea8026Sopenharmony_ci#ifndef LFS_READONLY
471019ea8026Sopenharmony_cistatic lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t pair[2],
471119ea8026Sopenharmony_ci        lfs_mdir_t *parent) {
471219ea8026Sopenharmony_ci    // use fetchmatch with callback to find pairs
471319ea8026Sopenharmony_ci    parent->tail[0] = 0;
471419ea8026Sopenharmony_ci    parent->tail[1] = 1;
471519ea8026Sopenharmony_ci    lfs_block_t tortoise[2] = {LFS_BLOCK_NULL, LFS_BLOCK_NULL};
471619ea8026Sopenharmony_ci    lfs_size_t tortoise_i = 1;
471719ea8026Sopenharmony_ci    lfs_size_t tortoise_period = 1;
471819ea8026Sopenharmony_ci    while (!lfs_pair_isnull(parent->tail)) {
471919ea8026Sopenharmony_ci        // detect cycles with Brent's algorithm
472019ea8026Sopenharmony_ci        if (lfs_pair_issync(parent->tail, tortoise)) {
472119ea8026Sopenharmony_ci            LFS_WARN("Cycle detected in tail list");
472219ea8026Sopenharmony_ci            return LFS_ERR_CORRUPT;
472319ea8026Sopenharmony_ci        }
472419ea8026Sopenharmony_ci        if (tortoise_i == tortoise_period) {
472519ea8026Sopenharmony_ci            tortoise[0] = parent->tail[0];
472619ea8026Sopenharmony_ci            tortoise[1] = parent->tail[1];
472719ea8026Sopenharmony_ci            tortoise_i = 0;
472819ea8026Sopenharmony_ci            tortoise_period *= 2;
472919ea8026Sopenharmony_ci        }
473019ea8026Sopenharmony_ci        tortoise_i += 1;
473119ea8026Sopenharmony_ci
473219ea8026Sopenharmony_ci        lfs_stag_t tag = lfs_dir_fetchmatch(lfs, parent, parent->tail,
473319ea8026Sopenharmony_ci                LFS_MKTAG(0x7ff, 0, 0x3ff),
473419ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_DIRSTRUCT, 0, 8),
473519ea8026Sopenharmony_ci                NULL,
473619ea8026Sopenharmony_ci                lfs_fs_parent_match, &(struct lfs_fs_parent_match){
473719ea8026Sopenharmony_ci                    lfs, {pair[0], pair[1]}});
473819ea8026Sopenharmony_ci        if (tag && tag != LFS_ERR_NOENT) {
473919ea8026Sopenharmony_ci            return tag;
474019ea8026Sopenharmony_ci        }
474119ea8026Sopenharmony_ci    }
474219ea8026Sopenharmony_ci
474319ea8026Sopenharmony_ci    return LFS_ERR_NOENT;
474419ea8026Sopenharmony_ci}
474519ea8026Sopenharmony_ci#endif
474619ea8026Sopenharmony_ci
474719ea8026Sopenharmony_cistatic void lfs_fs_prepsuperblock(lfs_t *lfs, bool needssuperblock) {
474819ea8026Sopenharmony_ci    lfs->gstate.tag = (lfs->gstate.tag & ~LFS_MKTAG(0, 0, 0x200))
474919ea8026Sopenharmony_ci            | (uint32_t)needssuperblock << 9;
475019ea8026Sopenharmony_ci}
475119ea8026Sopenharmony_ci
475219ea8026Sopenharmony_ci#ifndef LFS_READONLY
475319ea8026Sopenharmony_cistatic int lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) {
475419ea8026Sopenharmony_ci    LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) > 0x000 || orphans >= 0);
475519ea8026Sopenharmony_ci    LFS_ASSERT(lfs_tag_size(lfs->gstate.tag) < 0x1ff || orphans <= 0);
475619ea8026Sopenharmony_ci    lfs->gstate.tag += orphans;
475719ea8026Sopenharmony_ci    lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x800, 0, 0)) |
475819ea8026Sopenharmony_ci            ((uint32_t)lfs_gstate_hasorphans(&lfs->gstate) << 31));
475919ea8026Sopenharmony_ci
476019ea8026Sopenharmony_ci    return 0;
476119ea8026Sopenharmony_ci}
476219ea8026Sopenharmony_ci#endif
476319ea8026Sopenharmony_ci
476419ea8026Sopenharmony_ci#ifndef LFS_READONLY
476519ea8026Sopenharmony_cistatic void lfs_fs_prepmove(lfs_t *lfs,
476619ea8026Sopenharmony_ci        uint16_t id, const lfs_block_t pair[2]) {
476719ea8026Sopenharmony_ci    lfs->gstate.tag = ((lfs->gstate.tag & ~LFS_MKTAG(0x7ff, 0x3ff, 0)) |
476819ea8026Sopenharmony_ci            ((id != 0x3ff) ? LFS_MKTAG(LFS_TYPE_DELETE, id, 0) : 0));
476919ea8026Sopenharmony_ci    lfs->gstate.pair[0] = (id != 0x3ff) ? pair[0] : 0;
477019ea8026Sopenharmony_ci    lfs->gstate.pair[1] = (id != 0x3ff) ? pair[1] : 0;
477119ea8026Sopenharmony_ci}
477219ea8026Sopenharmony_ci#endif
477319ea8026Sopenharmony_ci
477419ea8026Sopenharmony_ci#ifndef LFS_READONLY
477519ea8026Sopenharmony_cistatic int lfs_fs_desuperblock(lfs_t *lfs) {
477619ea8026Sopenharmony_ci    if (!lfs_gstate_needssuperblock(&lfs->gstate)) {
477719ea8026Sopenharmony_ci        return 0;
477819ea8026Sopenharmony_ci    }
477919ea8026Sopenharmony_ci
478019ea8026Sopenharmony_ci    LFS_DEBUG("Rewriting superblock {0x%"PRIx32", 0x%"PRIx32"}",
478119ea8026Sopenharmony_ci            lfs->root[0],
478219ea8026Sopenharmony_ci            lfs->root[1]);
478319ea8026Sopenharmony_ci
478419ea8026Sopenharmony_ci    lfs_mdir_t root;
478519ea8026Sopenharmony_ci    int err = lfs_dir_fetch(lfs, &root, lfs->root);
478619ea8026Sopenharmony_ci    if (err) {
478719ea8026Sopenharmony_ci        return err;
478819ea8026Sopenharmony_ci    }
478919ea8026Sopenharmony_ci
479019ea8026Sopenharmony_ci    // write a new superblock
479119ea8026Sopenharmony_ci    lfs_superblock_t superblock = {
479219ea8026Sopenharmony_ci        .version     = lfs_fs_disk_version(lfs),
479319ea8026Sopenharmony_ci        .block_size  = lfs->cfg->block_size,
479419ea8026Sopenharmony_ci        .block_count = lfs->block_count,
479519ea8026Sopenharmony_ci        .name_max    = lfs->name_max,
479619ea8026Sopenharmony_ci        .file_max    = lfs->file_max,
479719ea8026Sopenharmony_ci        .attr_max    = lfs->attr_max,
479819ea8026Sopenharmony_ci    };
479919ea8026Sopenharmony_ci
480019ea8026Sopenharmony_ci    lfs_superblock_tole32(&superblock);
480119ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
480219ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
480319ea8026Sopenharmony_ci                &superblock}));
480419ea8026Sopenharmony_ci    if (err) {
480519ea8026Sopenharmony_ci        return err;
480619ea8026Sopenharmony_ci    }
480719ea8026Sopenharmony_ci
480819ea8026Sopenharmony_ci    lfs_fs_prepsuperblock(lfs, false);
480919ea8026Sopenharmony_ci    return 0;
481019ea8026Sopenharmony_ci}
481119ea8026Sopenharmony_ci#endif
481219ea8026Sopenharmony_ci
481319ea8026Sopenharmony_ci#ifndef LFS_READONLY
481419ea8026Sopenharmony_cistatic int lfs_fs_demove(lfs_t *lfs) {
481519ea8026Sopenharmony_ci    if (!lfs_gstate_hasmove(&lfs->gdisk)) {
481619ea8026Sopenharmony_ci        return 0;
481719ea8026Sopenharmony_ci    }
481819ea8026Sopenharmony_ci
481919ea8026Sopenharmony_ci    // Fix bad moves
482019ea8026Sopenharmony_ci    LFS_DEBUG("Fixing move {0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16,
482119ea8026Sopenharmony_ci            lfs->gdisk.pair[0],
482219ea8026Sopenharmony_ci            lfs->gdisk.pair[1],
482319ea8026Sopenharmony_ci            lfs_tag_id(lfs->gdisk.tag));
482419ea8026Sopenharmony_ci
482519ea8026Sopenharmony_ci    // no other gstate is supported at this time, so if we found something else
482619ea8026Sopenharmony_ci    // something most likely went wrong in gstate calculation
482719ea8026Sopenharmony_ci    LFS_ASSERT(lfs_tag_type3(lfs->gdisk.tag) == LFS_TYPE_DELETE);
482819ea8026Sopenharmony_ci
482919ea8026Sopenharmony_ci    // fetch and delete the moved entry
483019ea8026Sopenharmony_ci    lfs_mdir_t movedir;
483119ea8026Sopenharmony_ci    int err = lfs_dir_fetch(lfs, &movedir, lfs->gdisk.pair);
483219ea8026Sopenharmony_ci    if (err) {
483319ea8026Sopenharmony_ci        return err;
483419ea8026Sopenharmony_ci    }
483519ea8026Sopenharmony_ci
483619ea8026Sopenharmony_ci    // prep gstate and delete move id
483719ea8026Sopenharmony_ci    uint16_t moveid = lfs_tag_id(lfs->gdisk.tag);
483819ea8026Sopenharmony_ci    lfs_fs_prepmove(lfs, 0x3ff, NULL);
483919ea8026Sopenharmony_ci    err = lfs_dir_commit(lfs, &movedir, LFS_MKATTRS(
484019ea8026Sopenharmony_ci            {LFS_MKTAG(LFS_TYPE_DELETE, moveid, 0), NULL}));
484119ea8026Sopenharmony_ci    if (err) {
484219ea8026Sopenharmony_ci        return err;
484319ea8026Sopenharmony_ci    }
484419ea8026Sopenharmony_ci
484519ea8026Sopenharmony_ci    return 0;
484619ea8026Sopenharmony_ci}
484719ea8026Sopenharmony_ci#endif
484819ea8026Sopenharmony_ci
484919ea8026Sopenharmony_ci#ifndef LFS_READONLY
485019ea8026Sopenharmony_cistatic int lfs_fs_deorphan(lfs_t *lfs, bool powerloss) {
485119ea8026Sopenharmony_ci    if (!lfs_gstate_hasorphans(&lfs->gstate)) {
485219ea8026Sopenharmony_ci        return 0;
485319ea8026Sopenharmony_ci    }
485419ea8026Sopenharmony_ci
485519ea8026Sopenharmony_ci    // Check for orphans in two separate passes:
485619ea8026Sopenharmony_ci    // - 1 for half-orphans (relocations)
485719ea8026Sopenharmony_ci    // - 2 for full-orphans (removes/renames)
485819ea8026Sopenharmony_ci    //
485919ea8026Sopenharmony_ci    // Two separate passes are needed as half-orphans can contain outdated
486019ea8026Sopenharmony_ci    // references to full-orphans, effectively hiding them from the deorphan
486119ea8026Sopenharmony_ci    // search.
486219ea8026Sopenharmony_ci    //
486319ea8026Sopenharmony_ci    int pass = 0;
486419ea8026Sopenharmony_ci    while (pass < 2) {
486519ea8026Sopenharmony_ci        // Fix any orphans
486619ea8026Sopenharmony_ci        lfs_mdir_t pdir = {.split = true, .tail = {0, 1}};
486719ea8026Sopenharmony_ci        lfs_mdir_t dir;
486819ea8026Sopenharmony_ci        bool moreorphans = false;
486919ea8026Sopenharmony_ci
487019ea8026Sopenharmony_ci        // iterate over all directory directory entries
487119ea8026Sopenharmony_ci        while (!lfs_pair_isnull(pdir.tail)) {
487219ea8026Sopenharmony_ci            int err = lfs_dir_fetch(lfs, &dir, pdir.tail);
487319ea8026Sopenharmony_ci            if (err) {
487419ea8026Sopenharmony_ci                return err;
487519ea8026Sopenharmony_ci            }
487619ea8026Sopenharmony_ci
487719ea8026Sopenharmony_ci            // check head blocks for orphans
487819ea8026Sopenharmony_ci            if (!pdir.split) {
487919ea8026Sopenharmony_ci                // check if we have a parent
488019ea8026Sopenharmony_ci                lfs_mdir_t parent;
488119ea8026Sopenharmony_ci                lfs_stag_t tag = lfs_fs_parent(lfs, pdir.tail, &parent);
488219ea8026Sopenharmony_ci                if (tag < 0 && tag != LFS_ERR_NOENT) {
488319ea8026Sopenharmony_ci                    return tag;
488419ea8026Sopenharmony_ci                }
488519ea8026Sopenharmony_ci
488619ea8026Sopenharmony_ci                if (pass == 0 && tag != LFS_ERR_NOENT) {
488719ea8026Sopenharmony_ci                    lfs_block_t pair[2];
488819ea8026Sopenharmony_ci                    lfs_stag_t state = lfs_dir_get(lfs, &parent,
488919ea8026Sopenharmony_ci                            LFS_MKTAG(0x7ff, 0x3ff, 0), tag, pair);
489019ea8026Sopenharmony_ci                    if (state < 0) {
489119ea8026Sopenharmony_ci                        return state;
489219ea8026Sopenharmony_ci                    }
489319ea8026Sopenharmony_ci                    lfs_pair_fromle32(pair);
489419ea8026Sopenharmony_ci
489519ea8026Sopenharmony_ci                    if (!lfs_pair_issync(pair, pdir.tail)) {
489619ea8026Sopenharmony_ci                        // we have desynced
489719ea8026Sopenharmony_ci                        LFS_DEBUG("Fixing half-orphan "
489819ea8026Sopenharmony_ci                                "{0x%"PRIx32", 0x%"PRIx32"} "
489919ea8026Sopenharmony_ci                                "-> {0x%"PRIx32", 0x%"PRIx32"}",
490019ea8026Sopenharmony_ci                                pdir.tail[0], pdir.tail[1], pair[0], pair[1]);
490119ea8026Sopenharmony_ci
490219ea8026Sopenharmony_ci                        // fix pending move in this pair? this looks like an
490319ea8026Sopenharmony_ci                        // optimization but is in fact _required_ since
490419ea8026Sopenharmony_ci                        // relocating may outdate the move.
490519ea8026Sopenharmony_ci                        uint16_t moveid = 0x3ff;
490619ea8026Sopenharmony_ci                        if (lfs_gstate_hasmovehere(&lfs->gstate, pdir.pair)) {
490719ea8026Sopenharmony_ci                            moveid = lfs_tag_id(lfs->gstate.tag);
490819ea8026Sopenharmony_ci                            LFS_DEBUG("Fixing move while fixing orphans "
490919ea8026Sopenharmony_ci                                    "{0x%"PRIx32", 0x%"PRIx32"} 0x%"PRIx16"\n",
491019ea8026Sopenharmony_ci                                    pdir.pair[0], pdir.pair[1], moveid);
491119ea8026Sopenharmony_ci                            lfs_fs_prepmove(lfs, 0x3ff, NULL);
491219ea8026Sopenharmony_ci                        }
491319ea8026Sopenharmony_ci
491419ea8026Sopenharmony_ci                        lfs_pair_tole32(pair);
491519ea8026Sopenharmony_ci                        state = lfs_dir_orphaningcommit(lfs, &pdir, LFS_MKATTRS(
491619ea8026Sopenharmony_ci                                {LFS_MKTAG_IF(moveid != 0x3ff,
491719ea8026Sopenharmony_ci                                    LFS_TYPE_DELETE, moveid, 0), NULL},
491819ea8026Sopenharmony_ci                                {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8),
491919ea8026Sopenharmony_ci                                    pair}));
492019ea8026Sopenharmony_ci                        lfs_pair_fromle32(pair);
492119ea8026Sopenharmony_ci                        if (state < 0) {
492219ea8026Sopenharmony_ci                            return state;
492319ea8026Sopenharmony_ci                        }
492419ea8026Sopenharmony_ci
492519ea8026Sopenharmony_ci                        // did our commit create more orphans?
492619ea8026Sopenharmony_ci                        if (state == LFS_OK_ORPHANED) {
492719ea8026Sopenharmony_ci                            moreorphans = true;
492819ea8026Sopenharmony_ci                        }
492919ea8026Sopenharmony_ci
493019ea8026Sopenharmony_ci                        // refetch tail
493119ea8026Sopenharmony_ci                        continue;
493219ea8026Sopenharmony_ci                    }
493319ea8026Sopenharmony_ci                }
493419ea8026Sopenharmony_ci
493519ea8026Sopenharmony_ci                // note we only check for full orphans if we may have had a
493619ea8026Sopenharmony_ci                // power-loss, otherwise orphans are created intentionally
493719ea8026Sopenharmony_ci                // during operations such as lfs_mkdir
493819ea8026Sopenharmony_ci                if (pass == 1 && tag == LFS_ERR_NOENT && powerloss) {
493919ea8026Sopenharmony_ci                    // we are an orphan
494019ea8026Sopenharmony_ci                    LFS_DEBUG("Fixing orphan {0x%"PRIx32", 0x%"PRIx32"}",
494119ea8026Sopenharmony_ci                            pdir.tail[0], pdir.tail[1]);
494219ea8026Sopenharmony_ci
494319ea8026Sopenharmony_ci                    // steal state
494419ea8026Sopenharmony_ci                    err = lfs_dir_getgstate(lfs, &dir, &lfs->gdelta);
494519ea8026Sopenharmony_ci                    if (err) {
494619ea8026Sopenharmony_ci                        return err;
494719ea8026Sopenharmony_ci                    }
494819ea8026Sopenharmony_ci
494919ea8026Sopenharmony_ci                    // steal tail
495019ea8026Sopenharmony_ci                    lfs_pair_tole32(dir.tail);
495119ea8026Sopenharmony_ci                    int state = lfs_dir_orphaningcommit(lfs, &pdir, LFS_MKATTRS(
495219ea8026Sopenharmony_ci                            {LFS_MKTAG(LFS_TYPE_TAIL + dir.split, 0x3ff, 8),
495319ea8026Sopenharmony_ci                                dir.tail}));
495419ea8026Sopenharmony_ci                    lfs_pair_fromle32(dir.tail);
495519ea8026Sopenharmony_ci                    if (state < 0) {
495619ea8026Sopenharmony_ci                        return state;
495719ea8026Sopenharmony_ci                    }
495819ea8026Sopenharmony_ci
495919ea8026Sopenharmony_ci                    // did our commit create more orphans?
496019ea8026Sopenharmony_ci                    if (state == LFS_OK_ORPHANED) {
496119ea8026Sopenharmony_ci                        moreorphans = true;
496219ea8026Sopenharmony_ci                    }
496319ea8026Sopenharmony_ci
496419ea8026Sopenharmony_ci                    // refetch tail
496519ea8026Sopenharmony_ci                    continue;
496619ea8026Sopenharmony_ci                }
496719ea8026Sopenharmony_ci            }
496819ea8026Sopenharmony_ci
496919ea8026Sopenharmony_ci            pdir = dir;
497019ea8026Sopenharmony_ci        }
497119ea8026Sopenharmony_ci
497219ea8026Sopenharmony_ci        pass = moreorphans ? 0 : pass+1;
497319ea8026Sopenharmony_ci    }
497419ea8026Sopenharmony_ci
497519ea8026Sopenharmony_ci    // mark orphans as fixed
497619ea8026Sopenharmony_ci    return lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate));
497719ea8026Sopenharmony_ci}
497819ea8026Sopenharmony_ci#endif
497919ea8026Sopenharmony_ci
498019ea8026Sopenharmony_ci#ifndef LFS_READONLY
498119ea8026Sopenharmony_cistatic int lfs_fs_forceconsistency(lfs_t *lfs) {
498219ea8026Sopenharmony_ci    int err = lfs_fs_desuperblock(lfs);
498319ea8026Sopenharmony_ci    if (err) {
498419ea8026Sopenharmony_ci        return err;
498519ea8026Sopenharmony_ci    }
498619ea8026Sopenharmony_ci
498719ea8026Sopenharmony_ci    err = lfs_fs_demove(lfs);
498819ea8026Sopenharmony_ci    if (err) {
498919ea8026Sopenharmony_ci        return err;
499019ea8026Sopenharmony_ci    }
499119ea8026Sopenharmony_ci
499219ea8026Sopenharmony_ci    err = lfs_fs_deorphan(lfs, true);
499319ea8026Sopenharmony_ci    if (err) {
499419ea8026Sopenharmony_ci        return err;
499519ea8026Sopenharmony_ci    }
499619ea8026Sopenharmony_ci
499719ea8026Sopenharmony_ci    return 0;
499819ea8026Sopenharmony_ci}
499919ea8026Sopenharmony_ci#endif
500019ea8026Sopenharmony_ci
500119ea8026Sopenharmony_ci#ifndef LFS_READONLY
500219ea8026Sopenharmony_ciint lfs_fs_rawmkconsistent(lfs_t *lfs) {
500319ea8026Sopenharmony_ci    // lfs_fs_forceconsistency does most of the work here
500419ea8026Sopenharmony_ci    int err = lfs_fs_forceconsistency(lfs);
500519ea8026Sopenharmony_ci    if (err) {
500619ea8026Sopenharmony_ci        return err;
500719ea8026Sopenharmony_ci    }
500819ea8026Sopenharmony_ci
500919ea8026Sopenharmony_ci    // do we have any pending gstate?
501019ea8026Sopenharmony_ci    lfs_gstate_t delta = {0};
501119ea8026Sopenharmony_ci    lfs_gstate_xor(&delta, &lfs->gdisk);
501219ea8026Sopenharmony_ci    lfs_gstate_xor(&delta, &lfs->gstate);
501319ea8026Sopenharmony_ci    if (!lfs_gstate_iszero(&delta)) {
501419ea8026Sopenharmony_ci        // lfs_dir_commit will implicitly write out any pending gstate
501519ea8026Sopenharmony_ci        lfs_mdir_t root;
501619ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &root, lfs->root);
501719ea8026Sopenharmony_ci        if (err) {
501819ea8026Sopenharmony_ci            return err;
501919ea8026Sopenharmony_ci        }
502019ea8026Sopenharmony_ci
502119ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &root, NULL, 0);
502219ea8026Sopenharmony_ci        if (err) {
502319ea8026Sopenharmony_ci            return err;
502419ea8026Sopenharmony_ci        }
502519ea8026Sopenharmony_ci    }
502619ea8026Sopenharmony_ci
502719ea8026Sopenharmony_ci    return 0;
502819ea8026Sopenharmony_ci}
502919ea8026Sopenharmony_ci#endif
503019ea8026Sopenharmony_ci
503119ea8026Sopenharmony_cistatic int lfs_fs_size_count(void *p, lfs_block_t block) {
503219ea8026Sopenharmony_ci    (void)block;
503319ea8026Sopenharmony_ci    lfs_size_t *size = p;
503419ea8026Sopenharmony_ci    *size += 1;
503519ea8026Sopenharmony_ci    return 0;
503619ea8026Sopenharmony_ci}
503719ea8026Sopenharmony_ci
503819ea8026Sopenharmony_cistatic lfs_ssize_t lfs_fs_rawsize(lfs_t *lfs) {
503919ea8026Sopenharmony_ci    lfs_size_t size = 0;
504019ea8026Sopenharmony_ci    int err = lfs_fs_rawtraverse(lfs, lfs_fs_size_count, &size, false);
504119ea8026Sopenharmony_ci    if (err) {
504219ea8026Sopenharmony_ci        return err;
504319ea8026Sopenharmony_ci    }
504419ea8026Sopenharmony_ci
504519ea8026Sopenharmony_ci    return size;
504619ea8026Sopenharmony_ci}
504719ea8026Sopenharmony_ci
504819ea8026Sopenharmony_ci#ifndef LFS_READONLY
504919ea8026Sopenharmony_ciint lfs_fs_rawgrow(lfs_t *lfs, lfs_size_t block_count) {
505019ea8026Sopenharmony_ci    // shrinking is not supported
505119ea8026Sopenharmony_ci    LFS_ASSERT(block_count >= lfs->block_count);
505219ea8026Sopenharmony_ci
505319ea8026Sopenharmony_ci    if (block_count > lfs->block_count) {
505419ea8026Sopenharmony_ci        lfs->block_count = block_count;
505519ea8026Sopenharmony_ci
505619ea8026Sopenharmony_ci        // fetch the root
505719ea8026Sopenharmony_ci        lfs_mdir_t root;
505819ea8026Sopenharmony_ci        int err = lfs_dir_fetch(lfs, &root, lfs->root);
505919ea8026Sopenharmony_ci        if (err) {
506019ea8026Sopenharmony_ci            return err;
506119ea8026Sopenharmony_ci        }
506219ea8026Sopenharmony_ci
506319ea8026Sopenharmony_ci        // update the superblock
506419ea8026Sopenharmony_ci        lfs_superblock_t superblock;
506519ea8026Sopenharmony_ci        lfs_stag_t tag = lfs_dir_get(lfs, &root, LFS_MKTAG(0x7ff, 0x3ff, 0),
506619ea8026Sopenharmony_ci                LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
506719ea8026Sopenharmony_ci                &superblock);
506819ea8026Sopenharmony_ci        if (tag < 0) {
506919ea8026Sopenharmony_ci            return tag;
507019ea8026Sopenharmony_ci        }
507119ea8026Sopenharmony_ci        lfs_superblock_fromle32(&superblock);
507219ea8026Sopenharmony_ci
507319ea8026Sopenharmony_ci        superblock.block_count = lfs->block_count;
507419ea8026Sopenharmony_ci
507519ea8026Sopenharmony_ci        lfs_superblock_tole32(&superblock);
507619ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &root, LFS_MKATTRS(
507719ea8026Sopenharmony_ci                {tag, &superblock}));
507819ea8026Sopenharmony_ci        if (err) {
507919ea8026Sopenharmony_ci            return err;
508019ea8026Sopenharmony_ci        }
508119ea8026Sopenharmony_ci    }
508219ea8026Sopenharmony_ci
508319ea8026Sopenharmony_ci    return 0;
508419ea8026Sopenharmony_ci}
508519ea8026Sopenharmony_ci#endif
508619ea8026Sopenharmony_ci
508719ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
508819ea8026Sopenharmony_ci////// Migration from littelfs v1 below this //////
508919ea8026Sopenharmony_ci
509019ea8026Sopenharmony_ci/// Version info ///
509119ea8026Sopenharmony_ci
509219ea8026Sopenharmony_ci// Software library version
509319ea8026Sopenharmony_ci// Major (top-nibble), incremented on backwards incompatible changes
509419ea8026Sopenharmony_ci// Minor (bottom-nibble), incremented on feature additions
509519ea8026Sopenharmony_ci#define LFS1_VERSION 0x00010007
509619ea8026Sopenharmony_ci#define LFS1_VERSION_MAJOR (0xffff & (LFS1_VERSION >> 16))
509719ea8026Sopenharmony_ci#define LFS1_VERSION_MINOR (0xffff & (LFS1_VERSION >>  0))
509819ea8026Sopenharmony_ci
509919ea8026Sopenharmony_ci// Version of On-disk data structures
510019ea8026Sopenharmony_ci// Major (top-nibble), incremented on backwards incompatible changes
510119ea8026Sopenharmony_ci// Minor (bottom-nibble), incremented on feature additions
510219ea8026Sopenharmony_ci#define LFS1_DISK_VERSION 0x00010001
510319ea8026Sopenharmony_ci#define LFS1_DISK_VERSION_MAJOR (0xffff & (LFS1_DISK_VERSION >> 16))
510419ea8026Sopenharmony_ci#define LFS1_DISK_VERSION_MINOR (0xffff & (LFS1_DISK_VERSION >>  0))
510519ea8026Sopenharmony_ci
510619ea8026Sopenharmony_ci
510719ea8026Sopenharmony_ci/// v1 Definitions ///
510819ea8026Sopenharmony_ci
510919ea8026Sopenharmony_ci// File types
511019ea8026Sopenharmony_cienum lfs1_type {
511119ea8026Sopenharmony_ci    LFS1_TYPE_REG        = 0x11,
511219ea8026Sopenharmony_ci    LFS1_TYPE_DIR        = 0x22,
511319ea8026Sopenharmony_ci    LFS1_TYPE_SUPERBLOCK = 0x2e,
511419ea8026Sopenharmony_ci};
511519ea8026Sopenharmony_ci
511619ea8026Sopenharmony_citypedef struct lfs1 {
511719ea8026Sopenharmony_ci    lfs_block_t root[2];
511819ea8026Sopenharmony_ci} lfs1_t;
511919ea8026Sopenharmony_ci
512019ea8026Sopenharmony_citypedef struct lfs1_entry {
512119ea8026Sopenharmony_ci    lfs_off_t off;
512219ea8026Sopenharmony_ci
512319ea8026Sopenharmony_ci    struct lfs1_disk_entry {
512419ea8026Sopenharmony_ci        uint8_t type;
512519ea8026Sopenharmony_ci        uint8_t elen;
512619ea8026Sopenharmony_ci        uint8_t alen;
512719ea8026Sopenharmony_ci        uint8_t nlen;
512819ea8026Sopenharmony_ci        union {
512919ea8026Sopenharmony_ci            struct {
513019ea8026Sopenharmony_ci                lfs_block_t head;
513119ea8026Sopenharmony_ci                lfs_size_t size;
513219ea8026Sopenharmony_ci            } file;
513319ea8026Sopenharmony_ci            lfs_block_t dir[2];
513419ea8026Sopenharmony_ci        } u;
513519ea8026Sopenharmony_ci    } d;
513619ea8026Sopenharmony_ci} lfs1_entry_t;
513719ea8026Sopenharmony_ci
513819ea8026Sopenharmony_citypedef struct lfs1_dir {
513919ea8026Sopenharmony_ci    struct lfs1_dir *next;
514019ea8026Sopenharmony_ci    lfs_block_t pair[2];
514119ea8026Sopenharmony_ci    lfs_off_t off;
514219ea8026Sopenharmony_ci
514319ea8026Sopenharmony_ci    lfs_block_t head[2];
514419ea8026Sopenharmony_ci    lfs_off_t pos;
514519ea8026Sopenharmony_ci
514619ea8026Sopenharmony_ci    struct lfs1_disk_dir {
514719ea8026Sopenharmony_ci        uint32_t rev;
514819ea8026Sopenharmony_ci        lfs_size_t size;
514919ea8026Sopenharmony_ci        lfs_block_t tail[2];
515019ea8026Sopenharmony_ci    } d;
515119ea8026Sopenharmony_ci} lfs1_dir_t;
515219ea8026Sopenharmony_ci
515319ea8026Sopenharmony_citypedef struct lfs1_superblock {
515419ea8026Sopenharmony_ci    lfs_off_t off;
515519ea8026Sopenharmony_ci
515619ea8026Sopenharmony_ci    struct lfs1_disk_superblock {
515719ea8026Sopenharmony_ci        uint8_t type;
515819ea8026Sopenharmony_ci        uint8_t elen;
515919ea8026Sopenharmony_ci        uint8_t alen;
516019ea8026Sopenharmony_ci        uint8_t nlen;
516119ea8026Sopenharmony_ci        lfs_block_t root[2];
516219ea8026Sopenharmony_ci        uint32_t block_size;
516319ea8026Sopenharmony_ci        uint32_t block_count;
516419ea8026Sopenharmony_ci        uint32_t version;
516519ea8026Sopenharmony_ci        char magic[8];
516619ea8026Sopenharmony_ci    } d;
516719ea8026Sopenharmony_ci} lfs1_superblock_t;
516819ea8026Sopenharmony_ci
516919ea8026Sopenharmony_ci
517019ea8026Sopenharmony_ci/// Low-level wrappers v1->v2 ///
517119ea8026Sopenharmony_cistatic void lfs1_crc(uint32_t *crc, const void *buffer, size_t size) {
517219ea8026Sopenharmony_ci    *crc = lfs_crc(*crc, buffer, size);
517319ea8026Sopenharmony_ci}
517419ea8026Sopenharmony_ci
517519ea8026Sopenharmony_cistatic int lfs1_bd_read(lfs_t *lfs, lfs_block_t block,
517619ea8026Sopenharmony_ci        lfs_off_t off, void *buffer, lfs_size_t size) {
517719ea8026Sopenharmony_ci    // if we ever do more than writes to alternating pairs,
517819ea8026Sopenharmony_ci    // this may need to consider pcache
517919ea8026Sopenharmony_ci    return lfs_bd_read(lfs, &lfs->pcache, &lfs->rcache, size,
518019ea8026Sopenharmony_ci            block, off, buffer, size);
518119ea8026Sopenharmony_ci}
518219ea8026Sopenharmony_ci
518319ea8026Sopenharmony_cistatic int lfs1_bd_crc(lfs_t *lfs, lfs_block_t block,
518419ea8026Sopenharmony_ci        lfs_off_t off, lfs_size_t size, uint32_t *crc) {
518519ea8026Sopenharmony_ci    for (lfs_off_t i = 0; i < size; i++) {
518619ea8026Sopenharmony_ci        uint8_t c;
518719ea8026Sopenharmony_ci        int err = lfs1_bd_read(lfs, block, off+i, &c, 1);
518819ea8026Sopenharmony_ci        if (err) {
518919ea8026Sopenharmony_ci            return err;
519019ea8026Sopenharmony_ci        }
519119ea8026Sopenharmony_ci
519219ea8026Sopenharmony_ci        lfs1_crc(crc, &c, 1);
519319ea8026Sopenharmony_ci    }
519419ea8026Sopenharmony_ci
519519ea8026Sopenharmony_ci    return 0;
519619ea8026Sopenharmony_ci}
519719ea8026Sopenharmony_ci
519819ea8026Sopenharmony_ci
519919ea8026Sopenharmony_ci/// Endian swapping functions ///
520019ea8026Sopenharmony_cistatic void lfs1_dir_fromle32(struct lfs1_disk_dir *d) {
520119ea8026Sopenharmony_ci    d->rev     = lfs_fromle32(d->rev);
520219ea8026Sopenharmony_ci    d->size    = lfs_fromle32(d->size);
520319ea8026Sopenharmony_ci    d->tail[0] = lfs_fromle32(d->tail[0]);
520419ea8026Sopenharmony_ci    d->tail[1] = lfs_fromle32(d->tail[1]);
520519ea8026Sopenharmony_ci}
520619ea8026Sopenharmony_ci
520719ea8026Sopenharmony_cistatic void lfs1_dir_tole32(struct lfs1_disk_dir *d) {
520819ea8026Sopenharmony_ci    d->rev     = lfs_tole32(d->rev);
520919ea8026Sopenharmony_ci    d->size    = lfs_tole32(d->size);
521019ea8026Sopenharmony_ci    d->tail[0] = lfs_tole32(d->tail[0]);
521119ea8026Sopenharmony_ci    d->tail[1] = lfs_tole32(d->tail[1]);
521219ea8026Sopenharmony_ci}
521319ea8026Sopenharmony_ci
521419ea8026Sopenharmony_cistatic void lfs1_entry_fromle32(struct lfs1_disk_entry *d) {
521519ea8026Sopenharmony_ci    d->u.dir[0] = lfs_fromle32(d->u.dir[0]);
521619ea8026Sopenharmony_ci    d->u.dir[1] = lfs_fromle32(d->u.dir[1]);
521719ea8026Sopenharmony_ci}
521819ea8026Sopenharmony_ci
521919ea8026Sopenharmony_cistatic void lfs1_entry_tole32(struct lfs1_disk_entry *d) {
522019ea8026Sopenharmony_ci    d->u.dir[0] = lfs_tole32(d->u.dir[0]);
522119ea8026Sopenharmony_ci    d->u.dir[1] = lfs_tole32(d->u.dir[1]);
522219ea8026Sopenharmony_ci}
522319ea8026Sopenharmony_ci
522419ea8026Sopenharmony_cistatic void lfs1_superblock_fromle32(struct lfs1_disk_superblock *d) {
522519ea8026Sopenharmony_ci    d->root[0]     = lfs_fromle32(d->root[0]);
522619ea8026Sopenharmony_ci    d->root[1]     = lfs_fromle32(d->root[1]);
522719ea8026Sopenharmony_ci    d->block_size  = lfs_fromle32(d->block_size);
522819ea8026Sopenharmony_ci    d->block_count = lfs_fromle32(d->block_count);
522919ea8026Sopenharmony_ci    d->version     = lfs_fromle32(d->version);
523019ea8026Sopenharmony_ci}
523119ea8026Sopenharmony_ci
523219ea8026Sopenharmony_ci
523319ea8026Sopenharmony_ci///// Metadata pair and directory operations ///
523419ea8026Sopenharmony_cistatic inline lfs_size_t lfs1_entry_size(const lfs1_entry_t *entry) {
523519ea8026Sopenharmony_ci    return 4 + entry->d.elen + entry->d.alen + entry->d.nlen;
523619ea8026Sopenharmony_ci}
523719ea8026Sopenharmony_ci
523819ea8026Sopenharmony_cistatic int lfs1_dir_fetch(lfs_t *lfs,
523919ea8026Sopenharmony_ci        lfs1_dir_t *dir, const lfs_block_t pair[2]) {
524019ea8026Sopenharmony_ci    // copy out pair, otherwise may be aliasing dir
524119ea8026Sopenharmony_ci    const lfs_block_t tpair[2] = {pair[0], pair[1]};
524219ea8026Sopenharmony_ci    bool valid = false;
524319ea8026Sopenharmony_ci
524419ea8026Sopenharmony_ci    // check both blocks for the most recent revision
524519ea8026Sopenharmony_ci    for (int i = 0; i < 2; i++) {
524619ea8026Sopenharmony_ci        struct lfs1_disk_dir test;
524719ea8026Sopenharmony_ci        int err = lfs1_bd_read(lfs, tpair[i], 0, &test, sizeof(test));
524819ea8026Sopenharmony_ci        lfs1_dir_fromle32(&test);
524919ea8026Sopenharmony_ci        if (err) {
525019ea8026Sopenharmony_ci            if (err == LFS_ERR_CORRUPT) {
525119ea8026Sopenharmony_ci                continue;
525219ea8026Sopenharmony_ci            }
525319ea8026Sopenharmony_ci            return err;
525419ea8026Sopenharmony_ci        }
525519ea8026Sopenharmony_ci
525619ea8026Sopenharmony_ci        if (valid && lfs_scmp(test.rev, dir->d.rev) < 0) {
525719ea8026Sopenharmony_ci            continue;
525819ea8026Sopenharmony_ci        }
525919ea8026Sopenharmony_ci
526019ea8026Sopenharmony_ci        if ((0x7fffffff & test.size) < sizeof(test)+4 ||
526119ea8026Sopenharmony_ci            (0x7fffffff & test.size) > lfs->cfg->block_size) {
526219ea8026Sopenharmony_ci            continue;
526319ea8026Sopenharmony_ci        }
526419ea8026Sopenharmony_ci
526519ea8026Sopenharmony_ci        uint32_t crc = 0xffffffff;
526619ea8026Sopenharmony_ci        lfs1_dir_tole32(&test);
526719ea8026Sopenharmony_ci        lfs1_crc(&crc, &test, sizeof(test));
526819ea8026Sopenharmony_ci        lfs1_dir_fromle32(&test);
526919ea8026Sopenharmony_ci        err = lfs1_bd_crc(lfs, tpair[i], sizeof(test),
527019ea8026Sopenharmony_ci                (0x7fffffff & test.size) - sizeof(test), &crc);
527119ea8026Sopenharmony_ci        if (err) {
527219ea8026Sopenharmony_ci            if (err == LFS_ERR_CORRUPT) {
527319ea8026Sopenharmony_ci                continue;
527419ea8026Sopenharmony_ci            }
527519ea8026Sopenharmony_ci            return err;
527619ea8026Sopenharmony_ci        }
527719ea8026Sopenharmony_ci
527819ea8026Sopenharmony_ci        if (crc != 0) {
527919ea8026Sopenharmony_ci            continue;
528019ea8026Sopenharmony_ci        }
528119ea8026Sopenharmony_ci
528219ea8026Sopenharmony_ci        valid = true;
528319ea8026Sopenharmony_ci
528419ea8026Sopenharmony_ci        // setup dir in case it's valid
528519ea8026Sopenharmony_ci        dir->pair[0] = tpair[(i+0) % 2];
528619ea8026Sopenharmony_ci        dir->pair[1] = tpair[(i+1) % 2];
528719ea8026Sopenharmony_ci        dir->off = sizeof(dir->d);
528819ea8026Sopenharmony_ci        dir->d = test;
528919ea8026Sopenharmony_ci    }
529019ea8026Sopenharmony_ci
529119ea8026Sopenharmony_ci    if (!valid) {
529219ea8026Sopenharmony_ci        LFS_ERROR("Corrupted dir pair at {0x%"PRIx32", 0x%"PRIx32"}",
529319ea8026Sopenharmony_ci                tpair[0], tpair[1]);
529419ea8026Sopenharmony_ci        return LFS_ERR_CORRUPT;
529519ea8026Sopenharmony_ci    }
529619ea8026Sopenharmony_ci
529719ea8026Sopenharmony_ci    return 0;
529819ea8026Sopenharmony_ci}
529919ea8026Sopenharmony_ci
530019ea8026Sopenharmony_cistatic int lfs1_dir_next(lfs_t *lfs, lfs1_dir_t *dir, lfs1_entry_t *entry) {
530119ea8026Sopenharmony_ci    while (dir->off + sizeof(entry->d) > (0x7fffffff & dir->d.size)-4) {
530219ea8026Sopenharmony_ci        if (!(0x80000000 & dir->d.size)) {
530319ea8026Sopenharmony_ci            entry->off = dir->off;
530419ea8026Sopenharmony_ci            return LFS_ERR_NOENT;
530519ea8026Sopenharmony_ci        }
530619ea8026Sopenharmony_ci
530719ea8026Sopenharmony_ci        int err = lfs1_dir_fetch(lfs, dir, dir->d.tail);
530819ea8026Sopenharmony_ci        if (err) {
530919ea8026Sopenharmony_ci            return err;
531019ea8026Sopenharmony_ci        }
531119ea8026Sopenharmony_ci
531219ea8026Sopenharmony_ci        dir->off = sizeof(dir->d);
531319ea8026Sopenharmony_ci        dir->pos += sizeof(dir->d) + 4;
531419ea8026Sopenharmony_ci    }
531519ea8026Sopenharmony_ci
531619ea8026Sopenharmony_ci    int err = lfs1_bd_read(lfs, dir->pair[0], dir->off,
531719ea8026Sopenharmony_ci            &entry->d, sizeof(entry->d));
531819ea8026Sopenharmony_ci    lfs1_entry_fromle32(&entry->d);
531919ea8026Sopenharmony_ci    if (err) {
532019ea8026Sopenharmony_ci        return err;
532119ea8026Sopenharmony_ci    }
532219ea8026Sopenharmony_ci
532319ea8026Sopenharmony_ci    entry->off = dir->off;
532419ea8026Sopenharmony_ci    dir->off += lfs1_entry_size(entry);
532519ea8026Sopenharmony_ci    dir->pos += lfs1_entry_size(entry);
532619ea8026Sopenharmony_ci    return 0;
532719ea8026Sopenharmony_ci}
532819ea8026Sopenharmony_ci
532919ea8026Sopenharmony_ci/// littlefs v1 specific operations ///
533019ea8026Sopenharmony_ciint lfs1_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) {
533119ea8026Sopenharmony_ci    if (lfs_pair_isnull(lfs->lfs1->root)) {
533219ea8026Sopenharmony_ci        return 0;
533319ea8026Sopenharmony_ci    }
533419ea8026Sopenharmony_ci
533519ea8026Sopenharmony_ci    // iterate over metadata pairs
533619ea8026Sopenharmony_ci    lfs1_dir_t dir;
533719ea8026Sopenharmony_ci    lfs1_entry_t entry;
533819ea8026Sopenharmony_ci    lfs_block_t cwd[2] = {0, 1};
533919ea8026Sopenharmony_ci
534019ea8026Sopenharmony_ci    while (true) {
534119ea8026Sopenharmony_ci        for (int i = 0; i < 2; i++) {
534219ea8026Sopenharmony_ci            int err = cb(data, cwd[i]);
534319ea8026Sopenharmony_ci            if (err) {
534419ea8026Sopenharmony_ci                return err;
534519ea8026Sopenharmony_ci            }
534619ea8026Sopenharmony_ci        }
534719ea8026Sopenharmony_ci
534819ea8026Sopenharmony_ci        int err = lfs1_dir_fetch(lfs, &dir, cwd);
534919ea8026Sopenharmony_ci        if (err) {
535019ea8026Sopenharmony_ci            return err;
535119ea8026Sopenharmony_ci        }
535219ea8026Sopenharmony_ci
535319ea8026Sopenharmony_ci        // iterate over contents
535419ea8026Sopenharmony_ci        while (dir.off + sizeof(entry.d) <= (0x7fffffff & dir.d.size)-4) {
535519ea8026Sopenharmony_ci            err = lfs1_bd_read(lfs, dir.pair[0], dir.off,
535619ea8026Sopenharmony_ci                    &entry.d, sizeof(entry.d));
535719ea8026Sopenharmony_ci            lfs1_entry_fromle32(&entry.d);
535819ea8026Sopenharmony_ci            if (err) {
535919ea8026Sopenharmony_ci                return err;
536019ea8026Sopenharmony_ci            }
536119ea8026Sopenharmony_ci
536219ea8026Sopenharmony_ci            dir.off += lfs1_entry_size(&entry);
536319ea8026Sopenharmony_ci            if ((0x70 & entry.d.type) == (0x70 & LFS1_TYPE_REG)) {
536419ea8026Sopenharmony_ci                err = lfs_ctz_traverse(lfs, NULL, &lfs->rcache,
536519ea8026Sopenharmony_ci                        entry.d.u.file.head, entry.d.u.file.size, cb, data);
536619ea8026Sopenharmony_ci                if (err) {
536719ea8026Sopenharmony_ci                    return err;
536819ea8026Sopenharmony_ci                }
536919ea8026Sopenharmony_ci            }
537019ea8026Sopenharmony_ci        }
537119ea8026Sopenharmony_ci
537219ea8026Sopenharmony_ci        // we also need to check if we contain a threaded v2 directory
537319ea8026Sopenharmony_ci        lfs_mdir_t dir2 = {.split=true, .tail={cwd[0], cwd[1]}};
537419ea8026Sopenharmony_ci        while (dir2.split) {
537519ea8026Sopenharmony_ci            err = lfs_dir_fetch(lfs, &dir2, dir2.tail);
537619ea8026Sopenharmony_ci            if (err) {
537719ea8026Sopenharmony_ci                break;
537819ea8026Sopenharmony_ci            }
537919ea8026Sopenharmony_ci
538019ea8026Sopenharmony_ci            for (int i = 0; i < 2; i++) {
538119ea8026Sopenharmony_ci                err = cb(data, dir2.pair[i]);
538219ea8026Sopenharmony_ci                if (err) {
538319ea8026Sopenharmony_ci                    return err;
538419ea8026Sopenharmony_ci                }
538519ea8026Sopenharmony_ci            }
538619ea8026Sopenharmony_ci        }
538719ea8026Sopenharmony_ci
538819ea8026Sopenharmony_ci        cwd[0] = dir.d.tail[0];
538919ea8026Sopenharmony_ci        cwd[1] = dir.d.tail[1];
539019ea8026Sopenharmony_ci
539119ea8026Sopenharmony_ci        if (lfs_pair_isnull(cwd)) {
539219ea8026Sopenharmony_ci            break;
539319ea8026Sopenharmony_ci        }
539419ea8026Sopenharmony_ci    }
539519ea8026Sopenharmony_ci
539619ea8026Sopenharmony_ci    return 0;
539719ea8026Sopenharmony_ci}
539819ea8026Sopenharmony_ci
539919ea8026Sopenharmony_cistatic int lfs1_moved(lfs_t *lfs, const void *e) {
540019ea8026Sopenharmony_ci    if (lfs_pair_isnull(lfs->lfs1->root)) {
540119ea8026Sopenharmony_ci        return 0;
540219ea8026Sopenharmony_ci    }
540319ea8026Sopenharmony_ci
540419ea8026Sopenharmony_ci    // skip superblock
540519ea8026Sopenharmony_ci    lfs1_dir_t cwd;
540619ea8026Sopenharmony_ci    int err = lfs1_dir_fetch(lfs, &cwd, (const lfs_block_t[2]){0, 1});
540719ea8026Sopenharmony_ci    if (err) {
540819ea8026Sopenharmony_ci        return err;
540919ea8026Sopenharmony_ci    }
541019ea8026Sopenharmony_ci
541119ea8026Sopenharmony_ci    // iterate over all directory directory entries
541219ea8026Sopenharmony_ci    lfs1_entry_t entry;
541319ea8026Sopenharmony_ci    while (!lfs_pair_isnull(cwd.d.tail)) {
541419ea8026Sopenharmony_ci        err = lfs1_dir_fetch(lfs, &cwd, cwd.d.tail);
541519ea8026Sopenharmony_ci        if (err) {
541619ea8026Sopenharmony_ci            return err;
541719ea8026Sopenharmony_ci        }
541819ea8026Sopenharmony_ci
541919ea8026Sopenharmony_ci        while (true) {
542019ea8026Sopenharmony_ci            err = lfs1_dir_next(lfs, &cwd, &entry);
542119ea8026Sopenharmony_ci            if (err && err != LFS_ERR_NOENT) {
542219ea8026Sopenharmony_ci                return err;
542319ea8026Sopenharmony_ci            }
542419ea8026Sopenharmony_ci
542519ea8026Sopenharmony_ci            if (err == LFS_ERR_NOENT) {
542619ea8026Sopenharmony_ci                break;
542719ea8026Sopenharmony_ci            }
542819ea8026Sopenharmony_ci
542919ea8026Sopenharmony_ci            if (!(0x80 & entry.d.type) &&
543019ea8026Sopenharmony_ci                 memcmp(&entry.d.u, e, sizeof(entry.d.u)) == 0) {
543119ea8026Sopenharmony_ci                return true;
543219ea8026Sopenharmony_ci            }
543319ea8026Sopenharmony_ci        }
543419ea8026Sopenharmony_ci    }
543519ea8026Sopenharmony_ci
543619ea8026Sopenharmony_ci    return false;
543719ea8026Sopenharmony_ci}
543819ea8026Sopenharmony_ci
543919ea8026Sopenharmony_ci/// Filesystem operations ///
544019ea8026Sopenharmony_cistatic int lfs1_mount(lfs_t *lfs, struct lfs1 *lfs1,
544119ea8026Sopenharmony_ci        const struct lfs_config *cfg) {
544219ea8026Sopenharmony_ci    int err = 0;
544319ea8026Sopenharmony_ci    {
544419ea8026Sopenharmony_ci        err = lfs_init(lfs, cfg);
544519ea8026Sopenharmony_ci        if (err) {
544619ea8026Sopenharmony_ci            return err;
544719ea8026Sopenharmony_ci        }
544819ea8026Sopenharmony_ci
544919ea8026Sopenharmony_ci        lfs->lfs1 = lfs1;
545019ea8026Sopenharmony_ci        lfs->lfs1->root[0] = LFS_BLOCK_NULL;
545119ea8026Sopenharmony_ci        lfs->lfs1->root[1] = LFS_BLOCK_NULL;
545219ea8026Sopenharmony_ci
545319ea8026Sopenharmony_ci        // setup free lookahead
545419ea8026Sopenharmony_ci        lfs->free.off = 0;
545519ea8026Sopenharmony_ci        lfs->free.size = 0;
545619ea8026Sopenharmony_ci        lfs->free.i = 0;
545719ea8026Sopenharmony_ci        lfs_alloc_ack(lfs);
545819ea8026Sopenharmony_ci
545919ea8026Sopenharmony_ci        // load superblock
546019ea8026Sopenharmony_ci        lfs1_dir_t dir;
546119ea8026Sopenharmony_ci        lfs1_superblock_t superblock;
546219ea8026Sopenharmony_ci        err = lfs1_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1});
546319ea8026Sopenharmony_ci        if (err && err != LFS_ERR_CORRUPT) {
546419ea8026Sopenharmony_ci            goto cleanup;
546519ea8026Sopenharmony_ci        }
546619ea8026Sopenharmony_ci
546719ea8026Sopenharmony_ci        if (!err) {
546819ea8026Sopenharmony_ci            err = lfs1_bd_read(lfs, dir.pair[0], sizeof(dir.d),
546919ea8026Sopenharmony_ci                    &superblock.d, sizeof(superblock.d));
547019ea8026Sopenharmony_ci            lfs1_superblock_fromle32(&superblock.d);
547119ea8026Sopenharmony_ci            if (err) {
547219ea8026Sopenharmony_ci                goto cleanup;
547319ea8026Sopenharmony_ci            }
547419ea8026Sopenharmony_ci
547519ea8026Sopenharmony_ci            lfs->lfs1->root[0] = superblock.d.root[0];
547619ea8026Sopenharmony_ci            lfs->lfs1->root[1] = superblock.d.root[1];
547719ea8026Sopenharmony_ci        }
547819ea8026Sopenharmony_ci
547919ea8026Sopenharmony_ci        if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) {
548019ea8026Sopenharmony_ci            LFS_ERROR("Invalid superblock at {0x%"PRIx32", 0x%"PRIx32"}",
548119ea8026Sopenharmony_ci                    0, 1);
548219ea8026Sopenharmony_ci            err = LFS_ERR_CORRUPT;
548319ea8026Sopenharmony_ci            goto cleanup;
548419ea8026Sopenharmony_ci        }
548519ea8026Sopenharmony_ci
548619ea8026Sopenharmony_ci        uint16_t major_version = (0xffff & (superblock.d.version >> 16));
548719ea8026Sopenharmony_ci        uint16_t minor_version = (0xffff & (superblock.d.version >>  0));
548819ea8026Sopenharmony_ci        if ((major_version != LFS1_DISK_VERSION_MAJOR ||
548919ea8026Sopenharmony_ci             minor_version > LFS1_DISK_VERSION_MINOR)) {
549019ea8026Sopenharmony_ci            LFS_ERROR("Invalid version v%d.%d", major_version, minor_version);
549119ea8026Sopenharmony_ci            err = LFS_ERR_INVAL;
549219ea8026Sopenharmony_ci            goto cleanup;
549319ea8026Sopenharmony_ci        }
549419ea8026Sopenharmony_ci
549519ea8026Sopenharmony_ci        return 0;
549619ea8026Sopenharmony_ci    }
549719ea8026Sopenharmony_ci
549819ea8026Sopenharmony_cicleanup:
549919ea8026Sopenharmony_ci    lfs_deinit(lfs);
550019ea8026Sopenharmony_ci    return err;
550119ea8026Sopenharmony_ci}
550219ea8026Sopenharmony_ci
550319ea8026Sopenharmony_cistatic int lfs1_unmount(lfs_t *lfs) {
550419ea8026Sopenharmony_ci    return lfs_deinit(lfs);
550519ea8026Sopenharmony_ci}
550619ea8026Sopenharmony_ci
550719ea8026Sopenharmony_ci/// v1 migration ///
550819ea8026Sopenharmony_cistatic int lfs_rawmigrate(lfs_t *lfs, const struct lfs_config *cfg) {
550919ea8026Sopenharmony_ci    struct lfs1 lfs1;
551019ea8026Sopenharmony_ci
551119ea8026Sopenharmony_ci    // Indeterminate filesystem size not allowed for migration.
551219ea8026Sopenharmony_ci    LFS_ASSERT(cfg->block_count != 0);
551319ea8026Sopenharmony_ci
551419ea8026Sopenharmony_ci    int err = lfs1_mount(lfs, &lfs1, cfg);
551519ea8026Sopenharmony_ci    if (err) {
551619ea8026Sopenharmony_ci        return err;
551719ea8026Sopenharmony_ci    }
551819ea8026Sopenharmony_ci
551919ea8026Sopenharmony_ci    {
552019ea8026Sopenharmony_ci        // iterate through each directory, copying over entries
552119ea8026Sopenharmony_ci        // into new directory
552219ea8026Sopenharmony_ci        lfs1_dir_t dir1;
552319ea8026Sopenharmony_ci        lfs_mdir_t dir2;
552419ea8026Sopenharmony_ci        dir1.d.tail[0] = lfs->lfs1->root[0];
552519ea8026Sopenharmony_ci        dir1.d.tail[1] = lfs->lfs1->root[1];
552619ea8026Sopenharmony_ci        while (!lfs_pair_isnull(dir1.d.tail)) {
552719ea8026Sopenharmony_ci            // iterate old dir
552819ea8026Sopenharmony_ci            err = lfs1_dir_fetch(lfs, &dir1, dir1.d.tail);
552919ea8026Sopenharmony_ci            if (err) {
553019ea8026Sopenharmony_ci                goto cleanup;
553119ea8026Sopenharmony_ci            }
553219ea8026Sopenharmony_ci
553319ea8026Sopenharmony_ci            // create new dir and bind as temporary pretend root
553419ea8026Sopenharmony_ci            err = lfs_dir_alloc(lfs, &dir2);
553519ea8026Sopenharmony_ci            if (err) {
553619ea8026Sopenharmony_ci                goto cleanup;
553719ea8026Sopenharmony_ci            }
553819ea8026Sopenharmony_ci
553919ea8026Sopenharmony_ci            dir2.rev = dir1.d.rev;
554019ea8026Sopenharmony_ci            dir1.head[0] = dir1.pair[0];
554119ea8026Sopenharmony_ci            dir1.head[1] = dir1.pair[1];
554219ea8026Sopenharmony_ci            lfs->root[0] = dir2.pair[0];
554319ea8026Sopenharmony_ci            lfs->root[1] = dir2.pair[1];
554419ea8026Sopenharmony_ci
554519ea8026Sopenharmony_ci            err = lfs_dir_commit(lfs, &dir2, NULL, 0);
554619ea8026Sopenharmony_ci            if (err) {
554719ea8026Sopenharmony_ci                goto cleanup;
554819ea8026Sopenharmony_ci            }
554919ea8026Sopenharmony_ci
555019ea8026Sopenharmony_ci            while (true) {
555119ea8026Sopenharmony_ci                lfs1_entry_t entry1;
555219ea8026Sopenharmony_ci                err = lfs1_dir_next(lfs, &dir1, &entry1);
555319ea8026Sopenharmony_ci                if (err && err != LFS_ERR_NOENT) {
555419ea8026Sopenharmony_ci                    goto cleanup;
555519ea8026Sopenharmony_ci                }
555619ea8026Sopenharmony_ci
555719ea8026Sopenharmony_ci                if (err == LFS_ERR_NOENT) {
555819ea8026Sopenharmony_ci                    break;
555919ea8026Sopenharmony_ci                }
556019ea8026Sopenharmony_ci
556119ea8026Sopenharmony_ci                // check that entry has not been moved
556219ea8026Sopenharmony_ci                if (entry1.d.type & 0x80) {
556319ea8026Sopenharmony_ci                    int moved = lfs1_moved(lfs, &entry1.d.u);
556419ea8026Sopenharmony_ci                    if (moved < 0) {
556519ea8026Sopenharmony_ci                        err = moved;
556619ea8026Sopenharmony_ci                        goto cleanup;
556719ea8026Sopenharmony_ci                    }
556819ea8026Sopenharmony_ci
556919ea8026Sopenharmony_ci                    if (moved) {
557019ea8026Sopenharmony_ci                        continue;
557119ea8026Sopenharmony_ci                    }
557219ea8026Sopenharmony_ci
557319ea8026Sopenharmony_ci                    entry1.d.type &= ~0x80;
557419ea8026Sopenharmony_ci                }
557519ea8026Sopenharmony_ci
557619ea8026Sopenharmony_ci                // also fetch name
557719ea8026Sopenharmony_ci                char name[LFS_NAME_MAX+1];
557819ea8026Sopenharmony_ci                memset(name, 0, sizeof(name));
557919ea8026Sopenharmony_ci                err = lfs1_bd_read(lfs, dir1.pair[0],
558019ea8026Sopenharmony_ci                        entry1.off + 4+entry1.d.elen+entry1.d.alen,
558119ea8026Sopenharmony_ci                        name, entry1.d.nlen);
558219ea8026Sopenharmony_ci                if (err) {
558319ea8026Sopenharmony_ci                    goto cleanup;
558419ea8026Sopenharmony_ci                }
558519ea8026Sopenharmony_ci
558619ea8026Sopenharmony_ci                bool isdir = (entry1.d.type == LFS1_TYPE_DIR);
558719ea8026Sopenharmony_ci
558819ea8026Sopenharmony_ci                // create entry in new dir
558919ea8026Sopenharmony_ci                err = lfs_dir_fetch(lfs, &dir2, lfs->root);
559019ea8026Sopenharmony_ci                if (err) {
559119ea8026Sopenharmony_ci                    goto cleanup;
559219ea8026Sopenharmony_ci                }
559319ea8026Sopenharmony_ci
559419ea8026Sopenharmony_ci                uint16_t id;
559519ea8026Sopenharmony_ci                err = lfs_dir_find(lfs, &dir2, &(const char*){name}, &id);
559619ea8026Sopenharmony_ci                if (!(err == LFS_ERR_NOENT && id != 0x3ff)) {
559719ea8026Sopenharmony_ci                    err = (err < 0) ? err : LFS_ERR_EXIST;
559819ea8026Sopenharmony_ci                    goto cleanup;
559919ea8026Sopenharmony_ci                }
560019ea8026Sopenharmony_ci
560119ea8026Sopenharmony_ci                lfs1_entry_tole32(&entry1.d);
560219ea8026Sopenharmony_ci                err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
560319ea8026Sopenharmony_ci                        {LFS_MKTAG(LFS_TYPE_CREATE, id, 0), NULL},
560419ea8026Sopenharmony_ci                        {LFS_MKTAG_IF_ELSE(isdir,
560519ea8026Sopenharmony_ci                            LFS_TYPE_DIR, id, entry1.d.nlen,
560619ea8026Sopenharmony_ci                            LFS_TYPE_REG, id, entry1.d.nlen),
560719ea8026Sopenharmony_ci                                name},
560819ea8026Sopenharmony_ci                        {LFS_MKTAG_IF_ELSE(isdir,
560919ea8026Sopenharmony_ci                            LFS_TYPE_DIRSTRUCT, id, sizeof(entry1.d.u),
561019ea8026Sopenharmony_ci                            LFS_TYPE_CTZSTRUCT, id, sizeof(entry1.d.u)),
561119ea8026Sopenharmony_ci                                &entry1.d.u}));
561219ea8026Sopenharmony_ci                lfs1_entry_fromle32(&entry1.d);
561319ea8026Sopenharmony_ci                if (err) {
561419ea8026Sopenharmony_ci                    goto cleanup;
561519ea8026Sopenharmony_ci                }
561619ea8026Sopenharmony_ci            }
561719ea8026Sopenharmony_ci
561819ea8026Sopenharmony_ci            if (!lfs_pair_isnull(dir1.d.tail)) {
561919ea8026Sopenharmony_ci                // find last block and update tail to thread into fs
562019ea8026Sopenharmony_ci                err = lfs_dir_fetch(lfs, &dir2, lfs->root);
562119ea8026Sopenharmony_ci                if (err) {
562219ea8026Sopenharmony_ci                    goto cleanup;
562319ea8026Sopenharmony_ci                }
562419ea8026Sopenharmony_ci
562519ea8026Sopenharmony_ci                while (dir2.split) {
562619ea8026Sopenharmony_ci                    err = lfs_dir_fetch(lfs, &dir2, dir2.tail);
562719ea8026Sopenharmony_ci                    if (err) {
562819ea8026Sopenharmony_ci                        goto cleanup;
562919ea8026Sopenharmony_ci                    }
563019ea8026Sopenharmony_ci                }
563119ea8026Sopenharmony_ci
563219ea8026Sopenharmony_ci                lfs_pair_tole32(dir2.pair);
563319ea8026Sopenharmony_ci                err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
563419ea8026Sopenharmony_ci                        {LFS_MKTAG(LFS_TYPE_SOFTTAIL, 0x3ff, 8), dir1.d.tail}));
563519ea8026Sopenharmony_ci                lfs_pair_fromle32(dir2.pair);
563619ea8026Sopenharmony_ci                if (err) {
563719ea8026Sopenharmony_ci                    goto cleanup;
563819ea8026Sopenharmony_ci                }
563919ea8026Sopenharmony_ci            }
564019ea8026Sopenharmony_ci
564119ea8026Sopenharmony_ci            // Copy over first block to thread into fs. Unfortunately
564219ea8026Sopenharmony_ci            // if this fails there is not much we can do.
564319ea8026Sopenharmony_ci            LFS_DEBUG("Migrating {0x%"PRIx32", 0x%"PRIx32"} "
564419ea8026Sopenharmony_ci                        "-> {0x%"PRIx32", 0x%"PRIx32"}",
564519ea8026Sopenharmony_ci                    lfs->root[0], lfs->root[1], dir1.head[0], dir1.head[1]);
564619ea8026Sopenharmony_ci
564719ea8026Sopenharmony_ci            err = lfs_bd_erase(lfs, dir1.head[1]);
564819ea8026Sopenharmony_ci            if (err) {
564919ea8026Sopenharmony_ci                goto cleanup;
565019ea8026Sopenharmony_ci            }
565119ea8026Sopenharmony_ci
565219ea8026Sopenharmony_ci            err = lfs_dir_fetch(lfs, &dir2, lfs->root);
565319ea8026Sopenharmony_ci            if (err) {
565419ea8026Sopenharmony_ci                goto cleanup;
565519ea8026Sopenharmony_ci            }
565619ea8026Sopenharmony_ci
565719ea8026Sopenharmony_ci            for (lfs_off_t i = 0; i < dir2.off; i++) {
565819ea8026Sopenharmony_ci                uint8_t dat;
565919ea8026Sopenharmony_ci                err = lfs_bd_read(lfs,
566019ea8026Sopenharmony_ci                        NULL, &lfs->rcache, dir2.off,
566119ea8026Sopenharmony_ci                        dir2.pair[0], i, &dat, 1);
566219ea8026Sopenharmony_ci                if (err) {
566319ea8026Sopenharmony_ci                    goto cleanup;
566419ea8026Sopenharmony_ci                }
566519ea8026Sopenharmony_ci
566619ea8026Sopenharmony_ci                err = lfs_bd_prog(lfs,
566719ea8026Sopenharmony_ci                        &lfs->pcache, &lfs->rcache, true,
566819ea8026Sopenharmony_ci                        dir1.head[1], i, &dat, 1);
566919ea8026Sopenharmony_ci                if (err) {
567019ea8026Sopenharmony_ci                    goto cleanup;
567119ea8026Sopenharmony_ci                }
567219ea8026Sopenharmony_ci            }
567319ea8026Sopenharmony_ci
567419ea8026Sopenharmony_ci            err = lfs_bd_flush(lfs, &lfs->pcache, &lfs->rcache, true);
567519ea8026Sopenharmony_ci            if (err) {
567619ea8026Sopenharmony_ci                goto cleanup;
567719ea8026Sopenharmony_ci            }
567819ea8026Sopenharmony_ci        }
567919ea8026Sopenharmony_ci
568019ea8026Sopenharmony_ci        // Create new superblock. This marks a successful migration!
568119ea8026Sopenharmony_ci        err = lfs1_dir_fetch(lfs, &dir1, (const lfs_block_t[2]){0, 1});
568219ea8026Sopenharmony_ci        if (err) {
568319ea8026Sopenharmony_ci            goto cleanup;
568419ea8026Sopenharmony_ci        }
568519ea8026Sopenharmony_ci
568619ea8026Sopenharmony_ci        dir2.pair[0] = dir1.pair[0];
568719ea8026Sopenharmony_ci        dir2.pair[1] = dir1.pair[1];
568819ea8026Sopenharmony_ci        dir2.rev = dir1.d.rev;
568919ea8026Sopenharmony_ci        dir2.off = sizeof(dir2.rev);
569019ea8026Sopenharmony_ci        dir2.etag = 0xffffffff;
569119ea8026Sopenharmony_ci        dir2.count = 0;
569219ea8026Sopenharmony_ci        dir2.tail[0] = lfs->lfs1->root[0];
569319ea8026Sopenharmony_ci        dir2.tail[1] = lfs->lfs1->root[1];
569419ea8026Sopenharmony_ci        dir2.erased = false;
569519ea8026Sopenharmony_ci        dir2.split = true;
569619ea8026Sopenharmony_ci
569719ea8026Sopenharmony_ci        lfs_superblock_t superblock = {
569819ea8026Sopenharmony_ci            .version     = LFS_DISK_VERSION,
569919ea8026Sopenharmony_ci            .block_size  = lfs->cfg->block_size,
570019ea8026Sopenharmony_ci            .block_count = lfs->cfg->block_count,
570119ea8026Sopenharmony_ci            .name_max    = lfs->name_max,
570219ea8026Sopenharmony_ci            .file_max    = lfs->file_max,
570319ea8026Sopenharmony_ci            .attr_max    = lfs->attr_max,
570419ea8026Sopenharmony_ci        };
570519ea8026Sopenharmony_ci
570619ea8026Sopenharmony_ci        lfs_superblock_tole32(&superblock);
570719ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &dir2, LFS_MKATTRS(
570819ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_CREATE, 0, 0), NULL},
570919ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_SUPERBLOCK, 0, 8), "littlefs"},
571019ea8026Sopenharmony_ci                {LFS_MKTAG(LFS_TYPE_INLINESTRUCT, 0, sizeof(superblock)),
571119ea8026Sopenharmony_ci                    &superblock}));
571219ea8026Sopenharmony_ci        if (err) {
571319ea8026Sopenharmony_ci            goto cleanup;
571419ea8026Sopenharmony_ci        }
571519ea8026Sopenharmony_ci
571619ea8026Sopenharmony_ci        // sanity check that fetch works
571719ea8026Sopenharmony_ci        err = lfs_dir_fetch(lfs, &dir2, (const lfs_block_t[2]){0, 1});
571819ea8026Sopenharmony_ci        if (err) {
571919ea8026Sopenharmony_ci            goto cleanup;
572019ea8026Sopenharmony_ci        }
572119ea8026Sopenharmony_ci
572219ea8026Sopenharmony_ci        // force compaction to prevent accidentally mounting v1
572319ea8026Sopenharmony_ci        dir2.erased = false;
572419ea8026Sopenharmony_ci        err = lfs_dir_commit(lfs, &dir2, NULL, 0);
572519ea8026Sopenharmony_ci        if (err) {
572619ea8026Sopenharmony_ci            goto cleanup;
572719ea8026Sopenharmony_ci        }
572819ea8026Sopenharmony_ci    }
572919ea8026Sopenharmony_ci
573019ea8026Sopenharmony_cicleanup:
573119ea8026Sopenharmony_ci    lfs1_unmount(lfs);
573219ea8026Sopenharmony_ci    return err;
573319ea8026Sopenharmony_ci}
573419ea8026Sopenharmony_ci
573519ea8026Sopenharmony_ci#endif
573619ea8026Sopenharmony_ci
573719ea8026Sopenharmony_ci
573819ea8026Sopenharmony_ci/// Public API wrappers ///
573919ea8026Sopenharmony_ci
574019ea8026Sopenharmony_ci// Here we can add tracing/thread safety easily
574119ea8026Sopenharmony_ci
574219ea8026Sopenharmony_ci// Thread-safe wrappers if enabled
574319ea8026Sopenharmony_ci#ifdef LFS_THREADSAFE
574419ea8026Sopenharmony_ci#define LFS_LOCK(cfg)   cfg->lock(cfg)
574519ea8026Sopenharmony_ci#define LFS_UNLOCK(cfg) cfg->unlock(cfg)
574619ea8026Sopenharmony_ci#else
574719ea8026Sopenharmony_ci#define LFS_LOCK(cfg)   ((void)cfg, 0)
574819ea8026Sopenharmony_ci#define LFS_UNLOCK(cfg) ((void)cfg)
574919ea8026Sopenharmony_ci#endif
575019ea8026Sopenharmony_ci
575119ea8026Sopenharmony_ci// Public API
575219ea8026Sopenharmony_ci#ifndef LFS_READONLY
575319ea8026Sopenharmony_ciint lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
575419ea8026Sopenharmony_ci    int err = LFS_LOCK(cfg);
575519ea8026Sopenharmony_ci    if (err) {
575619ea8026Sopenharmony_ci        return err;
575719ea8026Sopenharmony_ci    }
575819ea8026Sopenharmony_ci    LFS_TRACE("lfs_format(%p, %p {.context=%p, "
575919ea8026Sopenharmony_ci                ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
576019ea8026Sopenharmony_ci                ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
576119ea8026Sopenharmony_ci                ".block_size=%"PRIu32", .block_count=%"PRIu32", "
576219ea8026Sopenharmony_ci                ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
576319ea8026Sopenharmony_ci                ".lookahead_size=%"PRIu32", .read_buffer=%p, "
576419ea8026Sopenharmony_ci                ".prog_buffer=%p, .lookahead_buffer=%p, "
576519ea8026Sopenharmony_ci                ".name_max=%"PRIu32", .file_max=%"PRIu32", "
576619ea8026Sopenharmony_ci                ".attr_max=%"PRIu32"})",
576719ea8026Sopenharmony_ci            (void*)lfs, (void*)cfg, cfg->context,
576819ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
576919ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
577019ea8026Sopenharmony_ci            cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
577119ea8026Sopenharmony_ci            cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
577219ea8026Sopenharmony_ci            cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
577319ea8026Sopenharmony_ci            cfg->name_max, cfg->file_max, cfg->attr_max);
577419ea8026Sopenharmony_ci
577519ea8026Sopenharmony_ci    err = lfs_rawformat(lfs, cfg);
577619ea8026Sopenharmony_ci
577719ea8026Sopenharmony_ci    LFS_TRACE("lfs_format -> %d", err);
577819ea8026Sopenharmony_ci    LFS_UNLOCK(cfg);
577919ea8026Sopenharmony_ci    return err;
578019ea8026Sopenharmony_ci}
578119ea8026Sopenharmony_ci#endif
578219ea8026Sopenharmony_ci
578319ea8026Sopenharmony_ciint lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) {
578419ea8026Sopenharmony_ci    int err = LFS_LOCK(cfg);
578519ea8026Sopenharmony_ci    if (err) {
578619ea8026Sopenharmony_ci        return err;
578719ea8026Sopenharmony_ci    }
578819ea8026Sopenharmony_ci    LFS_TRACE("lfs_mount(%p, %p {.context=%p, "
578919ea8026Sopenharmony_ci                ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
579019ea8026Sopenharmony_ci                ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
579119ea8026Sopenharmony_ci                ".block_size=%"PRIu32", .block_count=%"PRIu32", "
579219ea8026Sopenharmony_ci                ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
579319ea8026Sopenharmony_ci                ".lookahead_size=%"PRIu32", .read_buffer=%p, "
579419ea8026Sopenharmony_ci                ".prog_buffer=%p, .lookahead_buffer=%p, "
579519ea8026Sopenharmony_ci                ".name_max=%"PRIu32", .file_max=%"PRIu32", "
579619ea8026Sopenharmony_ci                ".attr_max=%"PRIu32"})",
579719ea8026Sopenharmony_ci            (void*)lfs, (void*)cfg, cfg->context,
579819ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
579919ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
580019ea8026Sopenharmony_ci            cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
580119ea8026Sopenharmony_ci            cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
580219ea8026Sopenharmony_ci            cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
580319ea8026Sopenharmony_ci            cfg->name_max, cfg->file_max, cfg->attr_max);
580419ea8026Sopenharmony_ci
580519ea8026Sopenharmony_ci    err = lfs_rawmount(lfs, cfg);
580619ea8026Sopenharmony_ci
580719ea8026Sopenharmony_ci    LFS_TRACE("lfs_mount -> %d", err);
580819ea8026Sopenharmony_ci    LFS_UNLOCK(cfg);
580919ea8026Sopenharmony_ci    return err;
581019ea8026Sopenharmony_ci}
581119ea8026Sopenharmony_ci
581219ea8026Sopenharmony_ciint lfs_unmount(lfs_t *lfs) {
581319ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
581419ea8026Sopenharmony_ci    if (err) {
581519ea8026Sopenharmony_ci        return err;
581619ea8026Sopenharmony_ci    }
581719ea8026Sopenharmony_ci    LFS_TRACE("lfs_unmount(%p)", (void*)lfs);
581819ea8026Sopenharmony_ci
581919ea8026Sopenharmony_ci    err = lfs_rawunmount(lfs);
582019ea8026Sopenharmony_ci
582119ea8026Sopenharmony_ci    LFS_TRACE("lfs_unmount -> %d", err);
582219ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
582319ea8026Sopenharmony_ci    return err;
582419ea8026Sopenharmony_ci}
582519ea8026Sopenharmony_ci
582619ea8026Sopenharmony_ci#ifndef LFS_READONLY
582719ea8026Sopenharmony_ciint lfs_remove(lfs_t *lfs, const char *path) {
582819ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
582919ea8026Sopenharmony_ci    if (err) {
583019ea8026Sopenharmony_ci        return err;
583119ea8026Sopenharmony_ci    }
583219ea8026Sopenharmony_ci    LFS_TRACE("lfs_remove(%p, \"%s\")", (void*)lfs, path);
583319ea8026Sopenharmony_ci
583419ea8026Sopenharmony_ci    err = lfs_rawremove(lfs, path);
583519ea8026Sopenharmony_ci
583619ea8026Sopenharmony_ci    LFS_TRACE("lfs_remove -> %d", err);
583719ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
583819ea8026Sopenharmony_ci    return err;
583919ea8026Sopenharmony_ci}
584019ea8026Sopenharmony_ci#endif
584119ea8026Sopenharmony_ci
584219ea8026Sopenharmony_ci#ifndef LFS_READONLY
584319ea8026Sopenharmony_ciint lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
584419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
584519ea8026Sopenharmony_ci    if (err) {
584619ea8026Sopenharmony_ci        return err;
584719ea8026Sopenharmony_ci    }
584819ea8026Sopenharmony_ci    LFS_TRACE("lfs_rename(%p, \"%s\", \"%s\")", (void*)lfs, oldpath, newpath);
584919ea8026Sopenharmony_ci
585019ea8026Sopenharmony_ci    err = lfs_rawrename(lfs, oldpath, newpath);
585119ea8026Sopenharmony_ci
585219ea8026Sopenharmony_ci    LFS_TRACE("lfs_rename -> %d", err);
585319ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
585419ea8026Sopenharmony_ci    return err;
585519ea8026Sopenharmony_ci}
585619ea8026Sopenharmony_ci#endif
585719ea8026Sopenharmony_ci
585819ea8026Sopenharmony_ciint lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
585919ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
586019ea8026Sopenharmony_ci    if (err) {
586119ea8026Sopenharmony_ci        return err;
586219ea8026Sopenharmony_ci    }
586319ea8026Sopenharmony_ci    LFS_TRACE("lfs_stat(%p, \"%s\", %p)", (void*)lfs, path, (void*)info);
586419ea8026Sopenharmony_ci
586519ea8026Sopenharmony_ci    err = lfs_rawstat(lfs, path, info);
586619ea8026Sopenharmony_ci
586719ea8026Sopenharmony_ci    LFS_TRACE("lfs_stat -> %d", err);
586819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
586919ea8026Sopenharmony_ci    return err;
587019ea8026Sopenharmony_ci}
587119ea8026Sopenharmony_ci
587219ea8026Sopenharmony_cilfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
587319ea8026Sopenharmony_ci        uint8_t type, void *buffer, lfs_size_t size) {
587419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
587519ea8026Sopenharmony_ci    if (err) {
587619ea8026Sopenharmony_ci        return err;
587719ea8026Sopenharmony_ci    }
587819ea8026Sopenharmony_ci    LFS_TRACE("lfs_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
587919ea8026Sopenharmony_ci            (void*)lfs, path, type, buffer, size);
588019ea8026Sopenharmony_ci
588119ea8026Sopenharmony_ci    lfs_ssize_t res = lfs_rawgetattr(lfs, path, type, buffer, size);
588219ea8026Sopenharmony_ci
588319ea8026Sopenharmony_ci    LFS_TRACE("lfs_getattr -> %"PRId32, res);
588419ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
588519ea8026Sopenharmony_ci    return res;
588619ea8026Sopenharmony_ci}
588719ea8026Sopenharmony_ci
588819ea8026Sopenharmony_ci#ifndef LFS_READONLY
588919ea8026Sopenharmony_ciint lfs_setattr(lfs_t *lfs, const char *path,
589019ea8026Sopenharmony_ci        uint8_t type, const void *buffer, lfs_size_t size) {
589119ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
589219ea8026Sopenharmony_ci    if (err) {
589319ea8026Sopenharmony_ci        return err;
589419ea8026Sopenharmony_ci    }
589519ea8026Sopenharmony_ci    LFS_TRACE("lfs_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")",
589619ea8026Sopenharmony_ci            (void*)lfs, path, type, buffer, size);
589719ea8026Sopenharmony_ci
589819ea8026Sopenharmony_ci    err = lfs_rawsetattr(lfs, path, type, buffer, size);
589919ea8026Sopenharmony_ci
590019ea8026Sopenharmony_ci    LFS_TRACE("lfs_setattr -> %d", err);
590119ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
590219ea8026Sopenharmony_ci    return err;
590319ea8026Sopenharmony_ci}
590419ea8026Sopenharmony_ci#endif
590519ea8026Sopenharmony_ci
590619ea8026Sopenharmony_ci#ifndef LFS_READONLY
590719ea8026Sopenharmony_ciint lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) {
590819ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
590919ea8026Sopenharmony_ci    if (err) {
591019ea8026Sopenharmony_ci        return err;
591119ea8026Sopenharmony_ci    }
591219ea8026Sopenharmony_ci    LFS_TRACE("lfs_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs, path, type);
591319ea8026Sopenharmony_ci
591419ea8026Sopenharmony_ci    err = lfs_rawremoveattr(lfs, path, type);
591519ea8026Sopenharmony_ci
591619ea8026Sopenharmony_ci    LFS_TRACE("lfs_removeattr -> %d", err);
591719ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
591819ea8026Sopenharmony_ci    return err;
591919ea8026Sopenharmony_ci}
592019ea8026Sopenharmony_ci#endif
592119ea8026Sopenharmony_ci
592219ea8026Sopenharmony_ci#ifndef LFS_NO_MALLOC
592319ea8026Sopenharmony_ciint lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) {
592419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
592519ea8026Sopenharmony_ci    if (err) {
592619ea8026Sopenharmony_ci        return err;
592719ea8026Sopenharmony_ci    }
592819ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_open(%p, %p, \"%s\", %x)",
592919ea8026Sopenharmony_ci            (void*)lfs, (void*)file, path, flags);
593019ea8026Sopenharmony_ci    LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
593119ea8026Sopenharmony_ci
593219ea8026Sopenharmony_ci    err = lfs_file_rawopen(lfs, file, path, flags);
593319ea8026Sopenharmony_ci
593419ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_open -> %d", err);
593519ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
593619ea8026Sopenharmony_ci    return err;
593719ea8026Sopenharmony_ci}
593819ea8026Sopenharmony_ci#endif
593919ea8026Sopenharmony_ci
594019ea8026Sopenharmony_ciint lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file,
594119ea8026Sopenharmony_ci        const char *path, int flags,
594219ea8026Sopenharmony_ci        const struct lfs_file_config *cfg) {
594319ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
594419ea8026Sopenharmony_ci    if (err) {
594519ea8026Sopenharmony_ci        return err;
594619ea8026Sopenharmony_ci    }
594719ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_opencfg(%p, %p, \"%s\", %x, %p {"
594819ea8026Sopenharmony_ci                 ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})",
594919ea8026Sopenharmony_ci            (void*)lfs, (void*)file, path, flags,
595019ea8026Sopenharmony_ci            (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count);
595119ea8026Sopenharmony_ci    LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
595219ea8026Sopenharmony_ci
595319ea8026Sopenharmony_ci    err = lfs_file_rawopencfg(lfs, file, path, flags, cfg);
595419ea8026Sopenharmony_ci
595519ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_opencfg -> %d", err);
595619ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
595719ea8026Sopenharmony_ci    return err;
595819ea8026Sopenharmony_ci}
595919ea8026Sopenharmony_ci
596019ea8026Sopenharmony_ciint lfs_file_close(lfs_t *lfs, lfs_file_t *file) {
596119ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
596219ea8026Sopenharmony_ci    if (err) {
596319ea8026Sopenharmony_ci        return err;
596419ea8026Sopenharmony_ci    }
596519ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_close(%p, %p)", (void*)lfs, (void*)file);
596619ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
596719ea8026Sopenharmony_ci
596819ea8026Sopenharmony_ci    err = lfs_file_rawclose(lfs, file);
596919ea8026Sopenharmony_ci
597019ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_close -> %d", err);
597119ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
597219ea8026Sopenharmony_ci    return err;
597319ea8026Sopenharmony_ci}
597419ea8026Sopenharmony_ci
597519ea8026Sopenharmony_ci#ifndef LFS_READONLY
597619ea8026Sopenharmony_ciint lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
597719ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
597819ea8026Sopenharmony_ci    if (err) {
597919ea8026Sopenharmony_ci        return err;
598019ea8026Sopenharmony_ci    }
598119ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_sync(%p, %p)", (void*)lfs, (void*)file);
598219ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
598319ea8026Sopenharmony_ci
598419ea8026Sopenharmony_ci    err = lfs_file_rawsync(lfs, file);
598519ea8026Sopenharmony_ci
598619ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_sync -> %d", err);
598719ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
598819ea8026Sopenharmony_ci    return err;
598919ea8026Sopenharmony_ci}
599019ea8026Sopenharmony_ci#endif
599119ea8026Sopenharmony_ci
599219ea8026Sopenharmony_cilfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
599319ea8026Sopenharmony_ci        void *buffer, lfs_size_t size) {
599419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
599519ea8026Sopenharmony_ci    if (err) {
599619ea8026Sopenharmony_ci        return err;
599719ea8026Sopenharmony_ci    }
599819ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_read(%p, %p, %p, %"PRIu32")",
599919ea8026Sopenharmony_ci            (void*)lfs, (void*)file, buffer, size);
600019ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
600119ea8026Sopenharmony_ci
600219ea8026Sopenharmony_ci    lfs_ssize_t res = lfs_file_rawread(lfs, file, buffer, size);
600319ea8026Sopenharmony_ci
600419ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_read -> %"PRId32, res);
600519ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
600619ea8026Sopenharmony_ci    return res;
600719ea8026Sopenharmony_ci}
600819ea8026Sopenharmony_ci
600919ea8026Sopenharmony_ci#ifndef LFS_READONLY
601019ea8026Sopenharmony_cilfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
601119ea8026Sopenharmony_ci        const void *buffer, lfs_size_t size) {
601219ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
601319ea8026Sopenharmony_ci    if (err) {
601419ea8026Sopenharmony_ci        return err;
601519ea8026Sopenharmony_ci    }
601619ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_write(%p, %p, %p, %"PRIu32")",
601719ea8026Sopenharmony_ci            (void*)lfs, (void*)file, buffer, size);
601819ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
601919ea8026Sopenharmony_ci
602019ea8026Sopenharmony_ci    lfs_ssize_t res = lfs_file_rawwrite(lfs, file, buffer, size);
602119ea8026Sopenharmony_ci
602219ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_write -> %"PRId32, res);
602319ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
602419ea8026Sopenharmony_ci    return res;
602519ea8026Sopenharmony_ci}
602619ea8026Sopenharmony_ci#endif
602719ea8026Sopenharmony_ci
602819ea8026Sopenharmony_cilfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
602919ea8026Sopenharmony_ci        lfs_soff_t off, int whence) {
603019ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
603119ea8026Sopenharmony_ci    if (err) {
603219ea8026Sopenharmony_ci        return err;
603319ea8026Sopenharmony_ci    }
603419ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_seek(%p, %p, %"PRId32", %d)",
603519ea8026Sopenharmony_ci            (void*)lfs, (void*)file, off, whence);
603619ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
603719ea8026Sopenharmony_ci
603819ea8026Sopenharmony_ci    lfs_soff_t res = lfs_file_rawseek(lfs, file, off, whence);
603919ea8026Sopenharmony_ci
604019ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_seek -> %"PRId32, res);
604119ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
604219ea8026Sopenharmony_ci    return res;
604319ea8026Sopenharmony_ci}
604419ea8026Sopenharmony_ci
604519ea8026Sopenharmony_ci#ifndef LFS_READONLY
604619ea8026Sopenharmony_ciint lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {
604719ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
604819ea8026Sopenharmony_ci    if (err) {
604919ea8026Sopenharmony_ci        return err;
605019ea8026Sopenharmony_ci    }
605119ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_truncate(%p, %p, %"PRIu32")",
605219ea8026Sopenharmony_ci            (void*)lfs, (void*)file, size);
605319ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
605419ea8026Sopenharmony_ci
605519ea8026Sopenharmony_ci    err = lfs_file_rawtruncate(lfs, file, size);
605619ea8026Sopenharmony_ci
605719ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_truncate -> %d", err);
605819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
605919ea8026Sopenharmony_ci    return err;
606019ea8026Sopenharmony_ci}
606119ea8026Sopenharmony_ci#endif
606219ea8026Sopenharmony_ci
606319ea8026Sopenharmony_cilfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) {
606419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
606519ea8026Sopenharmony_ci    if (err) {
606619ea8026Sopenharmony_ci        return err;
606719ea8026Sopenharmony_ci    }
606819ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_tell(%p, %p)", (void*)lfs, (void*)file);
606919ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
607019ea8026Sopenharmony_ci
607119ea8026Sopenharmony_ci    lfs_soff_t res = lfs_file_rawtell(lfs, file);
607219ea8026Sopenharmony_ci
607319ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_tell -> %"PRId32, res);
607419ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
607519ea8026Sopenharmony_ci    return res;
607619ea8026Sopenharmony_ci}
607719ea8026Sopenharmony_ci
607819ea8026Sopenharmony_ciint lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) {
607919ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
608019ea8026Sopenharmony_ci    if (err) {
608119ea8026Sopenharmony_ci        return err;
608219ea8026Sopenharmony_ci    }
608319ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_rewind(%p, %p)", (void*)lfs, (void*)file);
608419ea8026Sopenharmony_ci
608519ea8026Sopenharmony_ci    err = lfs_file_rawrewind(lfs, file);
608619ea8026Sopenharmony_ci
608719ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_rewind -> %d", err);
608819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
608919ea8026Sopenharmony_ci    return err;
609019ea8026Sopenharmony_ci}
609119ea8026Sopenharmony_ci
609219ea8026Sopenharmony_cilfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) {
609319ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
609419ea8026Sopenharmony_ci    if (err) {
609519ea8026Sopenharmony_ci        return err;
609619ea8026Sopenharmony_ci    }
609719ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_size(%p, %p)", (void*)lfs, (void*)file);
609819ea8026Sopenharmony_ci    LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file));
609919ea8026Sopenharmony_ci
610019ea8026Sopenharmony_ci    lfs_soff_t res = lfs_file_rawsize(lfs, file);
610119ea8026Sopenharmony_ci
610219ea8026Sopenharmony_ci    LFS_TRACE("lfs_file_size -> %"PRId32, res);
610319ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
610419ea8026Sopenharmony_ci    return res;
610519ea8026Sopenharmony_ci}
610619ea8026Sopenharmony_ci
610719ea8026Sopenharmony_ci#ifndef LFS_READONLY
610819ea8026Sopenharmony_ciint lfs_mkdir(lfs_t *lfs, const char *path) {
610919ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
611019ea8026Sopenharmony_ci    if (err) {
611119ea8026Sopenharmony_ci        return err;
611219ea8026Sopenharmony_ci    }
611319ea8026Sopenharmony_ci    LFS_TRACE("lfs_mkdir(%p, \"%s\")", (void*)lfs, path);
611419ea8026Sopenharmony_ci
611519ea8026Sopenharmony_ci    err = lfs_rawmkdir(lfs, path);
611619ea8026Sopenharmony_ci
611719ea8026Sopenharmony_ci    LFS_TRACE("lfs_mkdir -> %d", err);
611819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
611919ea8026Sopenharmony_ci    return err;
612019ea8026Sopenharmony_ci}
612119ea8026Sopenharmony_ci#endif
612219ea8026Sopenharmony_ci
612319ea8026Sopenharmony_ciint lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
612419ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
612519ea8026Sopenharmony_ci    if (err) {
612619ea8026Sopenharmony_ci        return err;
612719ea8026Sopenharmony_ci    }
612819ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_open(%p, %p, \"%s\")", (void*)lfs, (void*)dir, path);
612919ea8026Sopenharmony_ci    LFS_ASSERT(!lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)dir));
613019ea8026Sopenharmony_ci
613119ea8026Sopenharmony_ci    err = lfs_dir_rawopen(lfs, dir, path);
613219ea8026Sopenharmony_ci
613319ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_open -> %d", err);
613419ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
613519ea8026Sopenharmony_ci    return err;
613619ea8026Sopenharmony_ci}
613719ea8026Sopenharmony_ci
613819ea8026Sopenharmony_ciint lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir) {
613919ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
614019ea8026Sopenharmony_ci    if (err) {
614119ea8026Sopenharmony_ci        return err;
614219ea8026Sopenharmony_ci    }
614319ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_close(%p, %p)", (void*)lfs, (void*)dir);
614419ea8026Sopenharmony_ci
614519ea8026Sopenharmony_ci    err = lfs_dir_rawclose(lfs, dir);
614619ea8026Sopenharmony_ci
614719ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_close -> %d", err);
614819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
614919ea8026Sopenharmony_ci    return err;
615019ea8026Sopenharmony_ci}
615119ea8026Sopenharmony_ci
615219ea8026Sopenharmony_ciint lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) {
615319ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
615419ea8026Sopenharmony_ci    if (err) {
615519ea8026Sopenharmony_ci        return err;
615619ea8026Sopenharmony_ci    }
615719ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_read(%p, %p, %p)",
615819ea8026Sopenharmony_ci            (void*)lfs, (void*)dir, (void*)info);
615919ea8026Sopenharmony_ci
616019ea8026Sopenharmony_ci    err = lfs_dir_rawread(lfs, dir, info);
616119ea8026Sopenharmony_ci
616219ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_read -> %d", err);
616319ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
616419ea8026Sopenharmony_ci    return err;
616519ea8026Sopenharmony_ci}
616619ea8026Sopenharmony_ci
616719ea8026Sopenharmony_ciint lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off) {
616819ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
616919ea8026Sopenharmony_ci    if (err) {
617019ea8026Sopenharmony_ci        return err;
617119ea8026Sopenharmony_ci    }
617219ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_seek(%p, %p, %"PRIu32")",
617319ea8026Sopenharmony_ci            (void*)lfs, (void*)dir, off);
617419ea8026Sopenharmony_ci
617519ea8026Sopenharmony_ci    err = lfs_dir_rawseek(lfs, dir, off);
617619ea8026Sopenharmony_ci
617719ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_seek -> %d", err);
617819ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
617919ea8026Sopenharmony_ci    return err;
618019ea8026Sopenharmony_ci}
618119ea8026Sopenharmony_ci
618219ea8026Sopenharmony_cilfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir) {
618319ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
618419ea8026Sopenharmony_ci    if (err) {
618519ea8026Sopenharmony_ci        return err;
618619ea8026Sopenharmony_ci    }
618719ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_tell(%p, %p)", (void*)lfs, (void*)dir);
618819ea8026Sopenharmony_ci
618919ea8026Sopenharmony_ci    lfs_soff_t res = lfs_dir_rawtell(lfs, dir);
619019ea8026Sopenharmony_ci
619119ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_tell -> %"PRId32, res);
619219ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
619319ea8026Sopenharmony_ci    return res;
619419ea8026Sopenharmony_ci}
619519ea8026Sopenharmony_ci
619619ea8026Sopenharmony_ciint lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir) {
619719ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
619819ea8026Sopenharmony_ci    if (err) {
619919ea8026Sopenharmony_ci        return err;
620019ea8026Sopenharmony_ci    }
620119ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_rewind(%p, %p)", (void*)lfs, (void*)dir);
620219ea8026Sopenharmony_ci
620319ea8026Sopenharmony_ci    err = lfs_dir_rawrewind(lfs, dir);
620419ea8026Sopenharmony_ci
620519ea8026Sopenharmony_ci    LFS_TRACE("lfs_dir_rewind -> %d", err);
620619ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
620719ea8026Sopenharmony_ci    return err;
620819ea8026Sopenharmony_ci}
620919ea8026Sopenharmony_ci
621019ea8026Sopenharmony_ciint lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo) {
621119ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
621219ea8026Sopenharmony_ci    if (err) {
621319ea8026Sopenharmony_ci        return err;
621419ea8026Sopenharmony_ci    }
621519ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_stat(%p, %p)", (void*)lfs, (void*)fsinfo);
621619ea8026Sopenharmony_ci
621719ea8026Sopenharmony_ci    err = lfs_fs_rawstat(lfs, fsinfo);
621819ea8026Sopenharmony_ci
621919ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_stat -> %d", err);
622019ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
622119ea8026Sopenharmony_ci    return err;
622219ea8026Sopenharmony_ci}
622319ea8026Sopenharmony_ci
622419ea8026Sopenharmony_cilfs_ssize_t lfs_fs_size(lfs_t *lfs) {
622519ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
622619ea8026Sopenharmony_ci    if (err) {
622719ea8026Sopenharmony_ci        return err;
622819ea8026Sopenharmony_ci    }
622919ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_size(%p)", (void*)lfs);
623019ea8026Sopenharmony_ci
623119ea8026Sopenharmony_ci    lfs_ssize_t res = lfs_fs_rawsize(lfs);
623219ea8026Sopenharmony_ci
623319ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_size -> %"PRId32, res);
623419ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
623519ea8026Sopenharmony_ci    return res;
623619ea8026Sopenharmony_ci}
623719ea8026Sopenharmony_ci
623819ea8026Sopenharmony_ciint lfs_fs_traverse(lfs_t *lfs, int (*cb)(void *, lfs_block_t), void *data) {
623919ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
624019ea8026Sopenharmony_ci    if (err) {
624119ea8026Sopenharmony_ci        return err;
624219ea8026Sopenharmony_ci    }
624319ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_traverse(%p, %p, %p)",
624419ea8026Sopenharmony_ci            (void*)lfs, (void*)(uintptr_t)cb, data);
624519ea8026Sopenharmony_ci
624619ea8026Sopenharmony_ci    err = lfs_fs_rawtraverse(lfs, cb, data, true);
624719ea8026Sopenharmony_ci
624819ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_traverse -> %d", err);
624919ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
625019ea8026Sopenharmony_ci    return err;
625119ea8026Sopenharmony_ci}
625219ea8026Sopenharmony_ci
625319ea8026Sopenharmony_ci#ifndef LFS_READONLY
625419ea8026Sopenharmony_ciint lfs_fs_gc(lfs_t *lfs) {
625519ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
625619ea8026Sopenharmony_ci    if (err) {
625719ea8026Sopenharmony_ci        return err;
625819ea8026Sopenharmony_ci    }
625919ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_gc(%p)", (void*)lfs);
626019ea8026Sopenharmony_ci
626119ea8026Sopenharmony_ci    err = lfs_fs_rawgc(lfs);
626219ea8026Sopenharmony_ci
626319ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_gc -> %d", err);
626419ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
626519ea8026Sopenharmony_ci    return err;
626619ea8026Sopenharmony_ci}
626719ea8026Sopenharmony_ci#endif
626819ea8026Sopenharmony_ci
626919ea8026Sopenharmony_ci#ifndef LFS_READONLY
627019ea8026Sopenharmony_ciint lfs_fs_mkconsistent(lfs_t *lfs) {
627119ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
627219ea8026Sopenharmony_ci    if (err) {
627319ea8026Sopenharmony_ci        return err;
627419ea8026Sopenharmony_ci    }
627519ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_mkconsistent(%p)", (void*)lfs);
627619ea8026Sopenharmony_ci
627719ea8026Sopenharmony_ci    err = lfs_fs_rawmkconsistent(lfs);
627819ea8026Sopenharmony_ci
627919ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_mkconsistent -> %d", err);
628019ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
628119ea8026Sopenharmony_ci    return err;
628219ea8026Sopenharmony_ci}
628319ea8026Sopenharmony_ci#endif
628419ea8026Sopenharmony_ci
628519ea8026Sopenharmony_ci#ifndef LFS_READONLY
628619ea8026Sopenharmony_ciint lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count) {
628719ea8026Sopenharmony_ci    int err = LFS_LOCK(lfs->cfg);
628819ea8026Sopenharmony_ci    if (err) {
628919ea8026Sopenharmony_ci        return err;
629019ea8026Sopenharmony_ci    }
629119ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_grow(%p, %"PRIu32")", (void*)lfs, block_count);
629219ea8026Sopenharmony_ci
629319ea8026Sopenharmony_ci    err = lfs_fs_rawgrow(lfs, block_count);
629419ea8026Sopenharmony_ci
629519ea8026Sopenharmony_ci    LFS_TRACE("lfs_fs_grow -> %d", err);
629619ea8026Sopenharmony_ci    LFS_UNLOCK(lfs->cfg);
629719ea8026Sopenharmony_ci    return err;
629819ea8026Sopenharmony_ci}
629919ea8026Sopenharmony_ci#endif
630019ea8026Sopenharmony_ci
630119ea8026Sopenharmony_ci#ifdef LFS_MIGRATE
630219ea8026Sopenharmony_ciint lfs_migrate(lfs_t *lfs, const struct lfs_config *cfg) {
630319ea8026Sopenharmony_ci    int err = LFS_LOCK(cfg);
630419ea8026Sopenharmony_ci    if (err) {
630519ea8026Sopenharmony_ci        return err;
630619ea8026Sopenharmony_ci    }
630719ea8026Sopenharmony_ci    LFS_TRACE("lfs_migrate(%p, %p {.context=%p, "
630819ea8026Sopenharmony_ci                ".read=%p, .prog=%p, .erase=%p, .sync=%p, "
630919ea8026Sopenharmony_ci                ".read_size=%"PRIu32", .prog_size=%"PRIu32", "
631019ea8026Sopenharmony_ci                ".block_size=%"PRIu32", .block_count=%"PRIu32", "
631119ea8026Sopenharmony_ci                ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", "
631219ea8026Sopenharmony_ci                ".lookahead_size=%"PRIu32", .read_buffer=%p, "
631319ea8026Sopenharmony_ci                ".prog_buffer=%p, .lookahead_buffer=%p, "
631419ea8026Sopenharmony_ci                ".name_max=%"PRIu32", .file_max=%"PRIu32", "
631519ea8026Sopenharmony_ci                ".attr_max=%"PRIu32"})",
631619ea8026Sopenharmony_ci            (void*)lfs, (void*)cfg, cfg->context,
631719ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog,
631819ea8026Sopenharmony_ci            (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync,
631919ea8026Sopenharmony_ci            cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count,
632019ea8026Sopenharmony_ci            cfg->block_cycles, cfg->cache_size, cfg->lookahead_size,
632119ea8026Sopenharmony_ci            cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer,
632219ea8026Sopenharmony_ci            cfg->name_max, cfg->file_max, cfg->attr_max);
632319ea8026Sopenharmony_ci
632419ea8026Sopenharmony_ci    err = lfs_rawmigrate(lfs, cfg);
632519ea8026Sopenharmony_ci
632619ea8026Sopenharmony_ci    LFS_TRACE("lfs_migrate -> %d", err);
632719ea8026Sopenharmony_ci    LFS_UNLOCK(cfg);
632819ea8026Sopenharmony_ci    return err;
632919ea8026Sopenharmony_ci}
633019ea8026Sopenharmony_ci#endif
633119ea8026Sopenharmony_ci
6332