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