18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018-2019 HUAWEI, Inc. 48c2ecf20Sopenharmony_ci * https://www.huawei.com/ 58c2ecf20Sopenharmony_ci * Created by Gao Xiang <gaoxiang25@huawei.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "internal.h" 88c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 98c2ecf20Sopenharmony_ci#include <trace/events/erofs.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ciint z_erofs_fill_inode(struct inode *inode) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci if (vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY) { 168c2ecf20Sopenharmony_ci vi->z_advise = 0; 178c2ecf20Sopenharmony_ci vi->z_algorithmtype[0] = 0; 188c2ecf20Sopenharmony_ci vi->z_algorithmtype[1] = 0; 198c2ecf20Sopenharmony_ci vi->z_logical_clusterbits = LOG_BLOCK_SIZE; 208c2ecf20Sopenharmony_ci vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits; 218c2ecf20Sopenharmony_ci vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits; 228c2ecf20Sopenharmony_ci set_bit(EROFS_I_Z_INITED_BIT, &vi->flags); 238c2ecf20Sopenharmony_ci } 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci inode->i_mapping->a_ops = &z_erofs_aops; 268c2ecf20Sopenharmony_ci return 0; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int z_erofs_fill_inode_lazy(struct inode *inode) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 328c2ecf20Sopenharmony_ci struct super_block *const sb = inode->i_sb; 338c2ecf20Sopenharmony_ci int err; 348c2ecf20Sopenharmony_ci erofs_off_t pos; 358c2ecf20Sopenharmony_ci struct page *page; 368c2ecf20Sopenharmony_ci void *kaddr; 378c2ecf20Sopenharmony_ci struct z_erofs_map_header *h; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (test_bit(EROFS_I_Z_INITED_BIT, &vi->flags)) { 408c2ecf20Sopenharmony_ci /* 418c2ecf20Sopenharmony_ci * paired with smp_mb() at the end of the function to ensure 428c2ecf20Sopenharmony_ci * fields will only be observed after the bit is set. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci smp_mb(); 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (wait_on_bit_lock(&vi->flags, EROFS_I_BL_Z_BIT, TASK_KILLABLE)) 498c2ecf20Sopenharmony_ci return -ERESTARTSYS; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci err = 0; 528c2ecf20Sopenharmony_ci if (test_bit(EROFS_I_Z_INITED_BIT, &vi->flags)) 538c2ecf20Sopenharmony_ci goto out_unlock; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci DBG_BUGON(vi->datalayout == EROFS_INODE_FLAT_COMPRESSION_LEGACY); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci pos = ALIGN(iloc(EROFS_SB(sb), vi->nid) + vi->inode_isize + 588c2ecf20Sopenharmony_ci vi->xattr_isize, 8); 598c2ecf20Sopenharmony_ci page = erofs_get_meta_page(sb, erofs_blknr(pos)); 608c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 618c2ecf20Sopenharmony_ci err = PTR_ERR(page); 628c2ecf20Sopenharmony_ci goto out_unlock; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci h = kaddr + erofs_blkoff(pos); 688c2ecf20Sopenharmony_ci vi->z_advise = le16_to_cpu(h->h_advise); 698c2ecf20Sopenharmony_ci vi->z_algorithmtype[0] = h->h_algorithmtype & 15; 708c2ecf20Sopenharmony_ci vi->z_algorithmtype[1] = h->h_algorithmtype >> 4; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (vi->z_algorithmtype[0] >= Z_EROFS_COMPRESSION_MAX) { 738c2ecf20Sopenharmony_ci erofs_err(sb, "unknown compression format %u for nid %llu, please upgrade kernel", 748c2ecf20Sopenharmony_ci vi->z_algorithmtype[0], vi->nid); 758c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 768c2ecf20Sopenharmony_ci goto unmap_done; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci vi->z_logical_clusterbits = LOG_BLOCK_SIZE + (h->h_clusterbits & 7); 808c2ecf20Sopenharmony_ci vi->z_physical_clusterbits[0] = vi->z_logical_clusterbits + 818c2ecf20Sopenharmony_ci ((h->h_clusterbits >> 3) & 3); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (vi->z_physical_clusterbits[0] != LOG_BLOCK_SIZE) { 848c2ecf20Sopenharmony_ci erofs_err(sb, "unsupported physical clusterbits %u for nid %llu, please upgrade kernel", 858c2ecf20Sopenharmony_ci vi->z_physical_clusterbits[0], vi->nid); 868c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 878c2ecf20Sopenharmony_ci goto unmap_done; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci vi->z_physical_clusterbits[1] = vi->z_logical_clusterbits + 918c2ecf20Sopenharmony_ci ((h->h_clusterbits >> 5) & 7); 928c2ecf20Sopenharmony_ci /* paired with smp_mb() at the beginning of the function */ 938c2ecf20Sopenharmony_ci smp_mb(); 948c2ecf20Sopenharmony_ci set_bit(EROFS_I_Z_INITED_BIT, &vi->flags); 958c2ecf20Sopenharmony_ciunmap_done: 968c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 978c2ecf20Sopenharmony_ci unlock_page(page); 988c2ecf20Sopenharmony_ci put_page(page); 998c2ecf20Sopenharmony_ciout_unlock: 1008c2ecf20Sopenharmony_ci clear_and_wake_up_bit(EROFS_I_BL_Z_BIT, &vi->flags); 1018c2ecf20Sopenharmony_ci return err; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct z_erofs_maprecorder { 1058c2ecf20Sopenharmony_ci struct inode *inode; 1068c2ecf20Sopenharmony_ci struct erofs_map_blocks *map; 1078c2ecf20Sopenharmony_ci void *kaddr; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci unsigned long lcn; 1108c2ecf20Sopenharmony_ci /* compression extent information gathered */ 1118c2ecf20Sopenharmony_ci u8 type; 1128c2ecf20Sopenharmony_ci u16 clusterofs; 1138c2ecf20Sopenharmony_ci u16 delta[2]; 1148c2ecf20Sopenharmony_ci erofs_blk_t pblk; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int z_erofs_reload_indexes(struct z_erofs_maprecorder *m, 1188c2ecf20Sopenharmony_ci erofs_blk_t eblk) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct super_block *const sb = m->inode->i_sb; 1218c2ecf20Sopenharmony_ci struct erofs_map_blocks *const map = m->map; 1228c2ecf20Sopenharmony_ci struct page *mpage = map->mpage; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (mpage) { 1258c2ecf20Sopenharmony_ci if (mpage->index == eblk) { 1268c2ecf20Sopenharmony_ci if (!m->kaddr) 1278c2ecf20Sopenharmony_ci m->kaddr = kmap_atomic(mpage); 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (m->kaddr) { 1328c2ecf20Sopenharmony_ci kunmap_atomic(m->kaddr); 1338c2ecf20Sopenharmony_ci m->kaddr = NULL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci put_page(mpage); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci mpage = erofs_get_meta_page(sb, eblk); 1398c2ecf20Sopenharmony_ci if (IS_ERR(mpage)) { 1408c2ecf20Sopenharmony_ci map->mpage = NULL; 1418c2ecf20Sopenharmony_ci return PTR_ERR(mpage); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci m->kaddr = kmap_atomic(mpage); 1448c2ecf20Sopenharmony_ci unlock_page(mpage); 1458c2ecf20Sopenharmony_ci map->mpage = mpage; 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int legacy_load_cluster_from_disk(struct z_erofs_maprecorder *m, 1508c2ecf20Sopenharmony_ci unsigned long lcn) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct inode *const inode = m->inode; 1538c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 1548c2ecf20Sopenharmony_ci const erofs_off_t ibase = iloc(EROFS_I_SB(inode), vi->nid); 1558c2ecf20Sopenharmony_ci const erofs_off_t pos = 1568c2ecf20Sopenharmony_ci Z_EROFS_VLE_LEGACY_INDEX_ALIGN(ibase + vi->inode_isize + 1578c2ecf20Sopenharmony_ci vi->xattr_isize) + 1588c2ecf20Sopenharmony_ci lcn * sizeof(struct z_erofs_vle_decompressed_index); 1598c2ecf20Sopenharmony_ci struct z_erofs_vle_decompressed_index *di; 1608c2ecf20Sopenharmony_ci unsigned int advise, type; 1618c2ecf20Sopenharmony_ci int err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci err = z_erofs_reload_indexes(m, erofs_blknr(pos)); 1648c2ecf20Sopenharmony_ci if (err) 1658c2ecf20Sopenharmony_ci return err; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci m->lcn = lcn; 1688c2ecf20Sopenharmony_ci di = m->kaddr + erofs_blkoff(pos); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci advise = le16_to_cpu(di->di_advise); 1718c2ecf20Sopenharmony_ci type = (advise >> Z_EROFS_VLE_DI_CLUSTER_TYPE_BIT) & 1728c2ecf20Sopenharmony_ci ((1 << Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) - 1); 1738c2ecf20Sopenharmony_ci switch (type) { 1748c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD: 1758c2ecf20Sopenharmony_ci m->clusterofs = 1 << vi->z_logical_clusterbits; 1768c2ecf20Sopenharmony_ci m->delta[0] = le16_to_cpu(di->di_u.delta[0]); 1778c2ecf20Sopenharmony_ci m->delta[1] = le16_to_cpu(di->di_u.delta[1]); 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN: 1808c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_HEAD: 1818c2ecf20Sopenharmony_ci m->clusterofs = le16_to_cpu(di->di_clusterofs); 1828c2ecf20Sopenharmony_ci if (m->clusterofs >= 1 << vi->z_logical_clusterbits) { 1838c2ecf20Sopenharmony_ci DBG_BUGON(1); 1848c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci m->pblk = le32_to_cpu(di->di_u.blkaddr); 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci DBG_BUGON(1); 1908c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci m->type = type; 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic unsigned int decode_compactedbits(unsigned int lobits, 1978c2ecf20Sopenharmony_ci unsigned int lomask, 1988c2ecf20Sopenharmony_ci u8 *in, unsigned int pos, u8 *type) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci const unsigned int v = get_unaligned_le32(in + pos / 8) >> (pos & 7); 2018c2ecf20Sopenharmony_ci const unsigned int lo = v & lomask; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci *type = (v >> lobits) & 3; 2048c2ecf20Sopenharmony_ci return lo; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int unpack_compacted_index(struct z_erofs_maprecorder *m, 2088c2ecf20Sopenharmony_ci unsigned int amortizedshift, 2098c2ecf20Sopenharmony_ci unsigned int eofs) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(m->inode); 2128c2ecf20Sopenharmony_ci const unsigned int lclusterbits = vi->z_logical_clusterbits; 2138c2ecf20Sopenharmony_ci const unsigned int lomask = (1 << lclusterbits) - 1; 2148c2ecf20Sopenharmony_ci unsigned int vcnt, base, lo, encodebits, nblk; 2158c2ecf20Sopenharmony_ci int i; 2168c2ecf20Sopenharmony_ci u8 *in, type; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (1 << amortizedshift == 4 && lclusterbits <= 14) 2198c2ecf20Sopenharmony_ci vcnt = 2; 2208c2ecf20Sopenharmony_ci else if (1 << amortizedshift == 2 && lclusterbits == 12) 2218c2ecf20Sopenharmony_ci vcnt = 16; 2228c2ecf20Sopenharmony_ci else 2238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci encodebits = ((vcnt << amortizedshift) - sizeof(__le32)) * 8 / vcnt; 2268c2ecf20Sopenharmony_ci base = round_down(eofs, vcnt << amortizedshift); 2278c2ecf20Sopenharmony_ci in = m->kaddr + base; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci i = (eofs - base) >> amortizedshift; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci lo = decode_compactedbits(lclusterbits, lomask, 2328c2ecf20Sopenharmony_ci in, encodebits * i, &type); 2338c2ecf20Sopenharmony_ci m->type = type; 2348c2ecf20Sopenharmony_ci if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) { 2358c2ecf20Sopenharmony_ci m->clusterofs = 1 << lclusterbits; 2368c2ecf20Sopenharmony_ci if (i + 1 != vcnt) { 2378c2ecf20Sopenharmony_ci m->delta[0] = lo; 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci /* 2418c2ecf20Sopenharmony_ci * since the last lcluster in the pack is special, 2428c2ecf20Sopenharmony_ci * of which lo saves delta[1] rather than delta[0]. 2438c2ecf20Sopenharmony_ci * Hence, get delta[0] by the previous lcluster indirectly. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci lo = decode_compactedbits(lclusterbits, lomask, 2468c2ecf20Sopenharmony_ci in, encodebits * (i - 1), &type); 2478c2ecf20Sopenharmony_ci if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) 2488c2ecf20Sopenharmony_ci lo = 0; 2498c2ecf20Sopenharmony_ci m->delta[0] = lo + 1; 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci m->clusterofs = lo; 2538c2ecf20Sopenharmony_ci m->delta[0] = 0; 2548c2ecf20Sopenharmony_ci /* figout out blkaddr (pblk) for HEAD lclusters */ 2558c2ecf20Sopenharmony_ci nblk = 1; 2568c2ecf20Sopenharmony_ci while (i > 0) { 2578c2ecf20Sopenharmony_ci --i; 2588c2ecf20Sopenharmony_ci lo = decode_compactedbits(lclusterbits, lomask, 2598c2ecf20Sopenharmony_ci in, encodebits * i, &type); 2608c2ecf20Sopenharmony_ci if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) 2618c2ecf20Sopenharmony_ci i -= lo; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (i >= 0) 2648c2ecf20Sopenharmony_ci ++nblk; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci in += (vcnt << amortizedshift) - sizeof(__le32); 2678c2ecf20Sopenharmony_ci m->pblk = le32_to_cpu(*(__le32 *)in) + nblk; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m, 2728c2ecf20Sopenharmony_ci unsigned long lcn) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct inode *const inode = m->inode; 2758c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 2768c2ecf20Sopenharmony_ci const erofs_off_t ebase = ALIGN(iloc(EROFS_I_SB(inode), vi->nid) + 2778c2ecf20Sopenharmony_ci vi->inode_isize + vi->xattr_isize, 8) + 2788c2ecf20Sopenharmony_ci sizeof(struct z_erofs_map_header); 2798c2ecf20Sopenharmony_ci const unsigned int totalidx = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ); 2808c2ecf20Sopenharmony_ci unsigned int compacted_4b_initial, compacted_2b; 2818c2ecf20Sopenharmony_ci unsigned int amortizedshift; 2828c2ecf20Sopenharmony_ci erofs_off_t pos; 2838c2ecf20Sopenharmony_ci int err; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (lcn >= totalidx) 2868c2ecf20Sopenharmony_ci return -EINVAL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci m->lcn = lcn; 2898c2ecf20Sopenharmony_ci /* used to align to 32-byte (compacted_2b) alignment */ 2908c2ecf20Sopenharmony_ci compacted_4b_initial = (32 - ebase % 32) / 4; 2918c2ecf20Sopenharmony_ci if (compacted_4b_initial == 32 / 4) 2928c2ecf20Sopenharmony_ci compacted_4b_initial = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (vi->z_advise & Z_EROFS_ADVISE_COMPACTED_2B) 2958c2ecf20Sopenharmony_ci compacted_2b = rounddown(totalidx - compacted_4b_initial, 16); 2968c2ecf20Sopenharmony_ci else 2978c2ecf20Sopenharmony_ci compacted_2b = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci pos = ebase; 3008c2ecf20Sopenharmony_ci if (lcn < compacted_4b_initial) { 3018c2ecf20Sopenharmony_ci amortizedshift = 2; 3028c2ecf20Sopenharmony_ci goto out; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci pos += compacted_4b_initial * 4; 3058c2ecf20Sopenharmony_ci lcn -= compacted_4b_initial; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (lcn < compacted_2b) { 3088c2ecf20Sopenharmony_ci amortizedshift = 1; 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci pos += compacted_2b * 2; 3128c2ecf20Sopenharmony_ci lcn -= compacted_2b; 3138c2ecf20Sopenharmony_ci amortizedshift = 2; 3148c2ecf20Sopenharmony_ciout: 3158c2ecf20Sopenharmony_ci pos += lcn * (1 << amortizedshift); 3168c2ecf20Sopenharmony_ci err = z_erofs_reload_indexes(m, erofs_blknr(pos)); 3178c2ecf20Sopenharmony_ci if (err) 3188c2ecf20Sopenharmony_ci return err; 3198c2ecf20Sopenharmony_ci return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos)); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m, 3238c2ecf20Sopenharmony_ci unsigned int lcn) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci const unsigned int datamode = EROFS_I(m->inode)->datalayout; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (datamode == EROFS_INODE_FLAT_COMPRESSION_LEGACY) 3288c2ecf20Sopenharmony_ci return legacy_load_cluster_from_disk(m, lcn); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (datamode == EROFS_INODE_FLAT_COMPRESSION) 3318c2ecf20Sopenharmony_ci return compacted_load_cluster_from_disk(m, lcn); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int z_erofs_extent_lookback(struct z_erofs_maprecorder *m, 3378c2ecf20Sopenharmony_ci unsigned int lookback_distance) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(m->inode); 3408c2ecf20Sopenharmony_ci struct erofs_map_blocks *const map = m->map; 3418c2ecf20Sopenharmony_ci const unsigned int lclusterbits = vi->z_logical_clusterbits; 3428c2ecf20Sopenharmony_ci unsigned long lcn = m->lcn; 3438c2ecf20Sopenharmony_ci int err; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (lcn < lookback_distance) { 3468c2ecf20Sopenharmony_ci erofs_err(m->inode->i_sb, 3478c2ecf20Sopenharmony_ci "bogus lookback distance @ nid %llu", vi->nid); 3488c2ecf20Sopenharmony_ci DBG_BUGON(1); 3498c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* load extent head logical cluster if needed */ 3538c2ecf20Sopenharmony_ci lcn -= lookback_distance; 3548c2ecf20Sopenharmony_ci err = z_erofs_load_cluster_from_disk(m, lcn); 3558c2ecf20Sopenharmony_ci if (err) 3568c2ecf20Sopenharmony_ci return err; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci switch (m->type) { 3598c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD: 3608c2ecf20Sopenharmony_ci if (!m->delta[0]) { 3618c2ecf20Sopenharmony_ci erofs_err(m->inode->i_sb, 3628c2ecf20Sopenharmony_ci "invalid lookback distance 0 @ nid %llu", 3638c2ecf20Sopenharmony_ci vi->nid); 3648c2ecf20Sopenharmony_ci DBG_BUGON(1); 3658c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci return z_erofs_extent_lookback(m, m->delta[0]); 3688c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN: 3698c2ecf20Sopenharmony_ci map->m_flags &= ~EROFS_MAP_ZIPPED; 3708c2ecf20Sopenharmony_ci fallthrough; 3718c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_HEAD: 3728c2ecf20Sopenharmony_ci map->m_la = (lcn << lclusterbits) | m->clusterofs; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci erofs_err(m->inode->i_sb, 3768c2ecf20Sopenharmony_ci "unknown type %u @ lcn %lu of nid %llu", 3778c2ecf20Sopenharmony_ci m->type, lcn, vi->nid); 3788c2ecf20Sopenharmony_ci DBG_BUGON(1); 3798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciint z_erofs_map_blocks_iter(struct inode *inode, 3858c2ecf20Sopenharmony_ci struct erofs_map_blocks *map, 3868c2ecf20Sopenharmony_ci int flags) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 3898c2ecf20Sopenharmony_ci struct z_erofs_maprecorder m = { 3908c2ecf20Sopenharmony_ci .inode = inode, 3918c2ecf20Sopenharmony_ci .map = map, 3928c2ecf20Sopenharmony_ci }; 3938c2ecf20Sopenharmony_ci int err = 0; 3948c2ecf20Sopenharmony_ci unsigned int lclusterbits, endoff; 3958c2ecf20Sopenharmony_ci unsigned long long ofs, end; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci trace_z_erofs_map_blocks_iter_enter(inode, map, flags); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* when trying to read beyond EOF, leave it unmapped */ 4008c2ecf20Sopenharmony_ci if (map->m_la >= inode->i_size) { 4018c2ecf20Sopenharmony_ci map->m_llen = map->m_la + 1 - inode->i_size; 4028c2ecf20Sopenharmony_ci map->m_la = inode->i_size; 4038c2ecf20Sopenharmony_ci map->m_flags = 0; 4048c2ecf20Sopenharmony_ci goto out; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci err = z_erofs_fill_inode_lazy(inode); 4088c2ecf20Sopenharmony_ci if (err) 4098c2ecf20Sopenharmony_ci goto out; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci lclusterbits = vi->z_logical_clusterbits; 4128c2ecf20Sopenharmony_ci ofs = map->m_la; 4138c2ecf20Sopenharmony_ci m.lcn = ofs >> lclusterbits; 4148c2ecf20Sopenharmony_ci endoff = ofs & ((1 << lclusterbits) - 1); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci err = z_erofs_load_cluster_from_disk(&m, m.lcn); 4178c2ecf20Sopenharmony_ci if (err) 4188c2ecf20Sopenharmony_ci goto unmap_out; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci map->m_flags = EROFS_MAP_ZIPPED; /* by default, compressed */ 4218c2ecf20Sopenharmony_ci end = (m.lcn + 1ULL) << lclusterbits; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci switch (m.type) { 4248c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN: 4258c2ecf20Sopenharmony_ci if (endoff >= m.clusterofs) 4268c2ecf20Sopenharmony_ci map->m_flags &= ~EROFS_MAP_ZIPPED; 4278c2ecf20Sopenharmony_ci fallthrough; 4288c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_HEAD: 4298c2ecf20Sopenharmony_ci if (endoff >= m.clusterofs) { 4308c2ecf20Sopenharmony_ci map->m_la = (m.lcn << lclusterbits) | m.clusterofs; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci /* m.lcn should be >= 1 if endoff < m.clusterofs */ 4348c2ecf20Sopenharmony_ci if (!m.lcn) { 4358c2ecf20Sopenharmony_ci erofs_err(inode->i_sb, 4368c2ecf20Sopenharmony_ci "invalid logical cluster 0 at nid %llu", 4378c2ecf20Sopenharmony_ci vi->nid); 4388c2ecf20Sopenharmony_ci err = -EFSCORRUPTED; 4398c2ecf20Sopenharmony_ci goto unmap_out; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci end = (m.lcn << lclusterbits) | m.clusterofs; 4428c2ecf20Sopenharmony_ci map->m_flags |= EROFS_MAP_FULL_MAPPED; 4438c2ecf20Sopenharmony_ci m.delta[0] = 1; 4448c2ecf20Sopenharmony_ci fallthrough; 4458c2ecf20Sopenharmony_ci case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD: 4468c2ecf20Sopenharmony_ci /* get the correspoinding first chunk */ 4478c2ecf20Sopenharmony_ci err = z_erofs_extent_lookback(&m, m.delta[0]); 4488c2ecf20Sopenharmony_ci if (err) 4498c2ecf20Sopenharmony_ci goto unmap_out; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci default: 4528c2ecf20Sopenharmony_ci erofs_err(inode->i_sb, 4538c2ecf20Sopenharmony_ci "unknown type %u @ offset %llu of nid %llu", 4548c2ecf20Sopenharmony_ci m.type, ofs, vi->nid); 4558c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 4568c2ecf20Sopenharmony_ci goto unmap_out; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci map->m_llen = end - map->m_la; 4608c2ecf20Sopenharmony_ci map->m_plen = 1 << lclusterbits; 4618c2ecf20Sopenharmony_ci map->m_pa = blknr_to_addr(m.pblk); 4628c2ecf20Sopenharmony_ci map->m_flags |= EROFS_MAP_MAPPED; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciunmap_out: 4658c2ecf20Sopenharmony_ci if (m.kaddr) 4668c2ecf20Sopenharmony_ci kunmap_atomic(m.kaddr); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciout: 4698c2ecf20Sopenharmony_ci erofs_dbg("%s, m_la %llu m_pa %llu m_llen %llu m_plen %llu m_flags 0%o", 4708c2ecf20Sopenharmony_ci __func__, map->m_la, map->m_pa, 4718c2ecf20Sopenharmony_ci map->m_llen, map->m_plen, map->m_flags); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci trace_z_erofs_map_blocks_iter_exit(inode, map, flags, err); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* aggressively BUG_ON iff CONFIG_EROFS_FS_DEBUG is on */ 4768c2ecf20Sopenharmony_ci DBG_BUGON(err < 0 && err != -ENOMEM); 4778c2ecf20Sopenharmony_ci return err; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 480