162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018-2019 HUAWEI, Inc. 462306a36Sopenharmony_ci * https://www.huawei.com/ 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "internal.h" 762306a36Sopenharmony_ci#include <asm/unaligned.h> 862306a36Sopenharmony_ci#include <trace/events/erofs.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct z_erofs_maprecorder { 1162306a36Sopenharmony_ci struct inode *inode; 1262306a36Sopenharmony_ci struct erofs_map_blocks *map; 1362306a36Sopenharmony_ci void *kaddr; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci unsigned long lcn; 1662306a36Sopenharmony_ci /* compression extent information gathered */ 1762306a36Sopenharmony_ci u8 type, headtype; 1862306a36Sopenharmony_ci u16 clusterofs; 1962306a36Sopenharmony_ci u16 delta[2]; 2062306a36Sopenharmony_ci erofs_blk_t pblk, compressedblks; 2162306a36Sopenharmony_ci erofs_off_t nextpackoff; 2262306a36Sopenharmony_ci bool partialref; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m, 2662306a36Sopenharmony_ci unsigned long lcn) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct inode *const inode = m->inode; 2962306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 3062306a36Sopenharmony_ci const erofs_off_t pos = Z_EROFS_FULL_INDEX_ALIGN(erofs_iloc(inode) + 3162306a36Sopenharmony_ci vi->inode_isize + vi->xattr_isize) + 3262306a36Sopenharmony_ci lcn * sizeof(struct z_erofs_lcluster_index); 3362306a36Sopenharmony_ci struct z_erofs_lcluster_index *di; 3462306a36Sopenharmony_ci unsigned int advise, type; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci m->kaddr = erofs_read_metabuf(&m->map->buf, inode->i_sb, 3762306a36Sopenharmony_ci erofs_blknr(inode->i_sb, pos), EROFS_KMAP); 3862306a36Sopenharmony_ci if (IS_ERR(m->kaddr)) 3962306a36Sopenharmony_ci return PTR_ERR(m->kaddr); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci m->nextpackoff = pos + sizeof(struct z_erofs_lcluster_index); 4262306a36Sopenharmony_ci m->lcn = lcn; 4362306a36Sopenharmony_ci di = m->kaddr + erofs_blkoff(inode->i_sb, pos); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci advise = le16_to_cpu(di->di_advise); 4662306a36Sopenharmony_ci type = (advise >> Z_EROFS_LI_LCLUSTER_TYPE_BIT) & 4762306a36Sopenharmony_ci ((1 << Z_EROFS_LI_LCLUSTER_TYPE_BITS) - 1); 4862306a36Sopenharmony_ci switch (type) { 4962306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_NONHEAD: 5062306a36Sopenharmony_ci m->clusterofs = 1 << vi->z_logical_clusterbits; 5162306a36Sopenharmony_ci m->delta[0] = le16_to_cpu(di->di_u.delta[0]); 5262306a36Sopenharmony_ci if (m->delta[0] & Z_EROFS_LI_D0_CBLKCNT) { 5362306a36Sopenharmony_ci if (!(vi->z_advise & (Z_EROFS_ADVISE_BIG_PCLUSTER_1 | 5462306a36Sopenharmony_ci Z_EROFS_ADVISE_BIG_PCLUSTER_2))) { 5562306a36Sopenharmony_ci DBG_BUGON(1); 5662306a36Sopenharmony_ci return -EFSCORRUPTED; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci m->compressedblks = m->delta[0] & 5962306a36Sopenharmony_ci ~Z_EROFS_LI_D0_CBLKCNT; 6062306a36Sopenharmony_ci m->delta[0] = 1; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci m->delta[1] = le16_to_cpu(di->di_u.delta[1]); 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_PLAIN: 6562306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD1: 6662306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD2: 6762306a36Sopenharmony_ci if (advise & Z_EROFS_LI_PARTIAL_REF) 6862306a36Sopenharmony_ci m->partialref = true; 6962306a36Sopenharmony_ci m->clusterofs = le16_to_cpu(di->di_clusterofs); 7062306a36Sopenharmony_ci if (m->clusterofs >= 1 << vi->z_logical_clusterbits) { 7162306a36Sopenharmony_ci DBG_BUGON(1); 7262306a36Sopenharmony_ci return -EFSCORRUPTED; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci m->pblk = le32_to_cpu(di->di_u.blkaddr); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci default: 7762306a36Sopenharmony_ci DBG_BUGON(1); 7862306a36Sopenharmony_ci return -EOPNOTSUPP; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci m->type = type; 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic unsigned int decode_compactedbits(unsigned int lobits, 8562306a36Sopenharmony_ci u8 *in, unsigned int pos, u8 *type) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci const unsigned int v = get_unaligned_le32(in + pos / 8) >> (pos & 7); 8862306a36Sopenharmony_ci const unsigned int lo = v & ((1 << lobits) - 1); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci *type = (v >> lobits) & 3; 9162306a36Sopenharmony_ci return lo; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int get_compacted_la_distance(unsigned int lobits, 9562306a36Sopenharmony_ci unsigned int encodebits, 9662306a36Sopenharmony_ci unsigned int vcnt, u8 *in, int i) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci unsigned int lo, d1 = 0; 9962306a36Sopenharmony_ci u8 type; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci DBG_BUGON(i >= vcnt); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci do { 10462306a36Sopenharmony_ci lo = decode_compactedbits(lobits, in, encodebits * i, &type); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (type != Z_EROFS_LCLUSTER_TYPE_NONHEAD) 10762306a36Sopenharmony_ci return d1; 10862306a36Sopenharmony_ci ++d1; 10962306a36Sopenharmony_ci } while (++i < vcnt); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* vcnt - 1 (Z_EROFS_LCLUSTER_TYPE_NONHEAD) item */ 11262306a36Sopenharmony_ci if (!(lo & Z_EROFS_LI_D0_CBLKCNT)) 11362306a36Sopenharmony_ci d1 += lo - 1; 11462306a36Sopenharmony_ci return d1; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int unpack_compacted_index(struct z_erofs_maprecorder *m, 11862306a36Sopenharmony_ci unsigned int amortizedshift, 11962306a36Sopenharmony_ci erofs_off_t pos, bool lookahead) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(m->inode); 12262306a36Sopenharmony_ci const unsigned int lclusterbits = vi->z_logical_clusterbits; 12362306a36Sopenharmony_ci unsigned int vcnt, base, lo, lobits, encodebits, nblk, eofs; 12462306a36Sopenharmony_ci int i; 12562306a36Sopenharmony_ci u8 *in, type; 12662306a36Sopenharmony_ci bool big_pcluster; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (1 << amortizedshift == 4 && lclusterbits <= 14) 12962306a36Sopenharmony_ci vcnt = 2; 13062306a36Sopenharmony_ci else if (1 << amortizedshift == 2 && lclusterbits <= 12) 13162306a36Sopenharmony_ci vcnt = 16; 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci return -EOPNOTSUPP; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* it doesn't equal to round_up(..) */ 13662306a36Sopenharmony_ci m->nextpackoff = round_down(pos, vcnt << amortizedshift) + 13762306a36Sopenharmony_ci (vcnt << amortizedshift); 13862306a36Sopenharmony_ci big_pcluster = vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1; 13962306a36Sopenharmony_ci lobits = max(lclusterbits, ilog2(Z_EROFS_LI_D0_CBLKCNT) + 1U); 14062306a36Sopenharmony_ci encodebits = ((vcnt << amortizedshift) - sizeof(__le32)) * 8 / vcnt; 14162306a36Sopenharmony_ci eofs = erofs_blkoff(m->inode->i_sb, pos); 14262306a36Sopenharmony_ci base = round_down(eofs, vcnt << amortizedshift); 14362306a36Sopenharmony_ci in = m->kaddr + base; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci i = (eofs - base) >> amortizedshift; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci lo = decode_compactedbits(lobits, in, encodebits * i, &type); 14862306a36Sopenharmony_ci m->type = type; 14962306a36Sopenharmony_ci if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) { 15062306a36Sopenharmony_ci m->clusterofs = 1 << lclusterbits; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* figure out lookahead_distance: delta[1] if needed */ 15362306a36Sopenharmony_ci if (lookahead) 15462306a36Sopenharmony_ci m->delta[1] = get_compacted_la_distance(lobits, 15562306a36Sopenharmony_ci encodebits, vcnt, in, i); 15662306a36Sopenharmony_ci if (lo & Z_EROFS_LI_D0_CBLKCNT) { 15762306a36Sopenharmony_ci if (!big_pcluster) { 15862306a36Sopenharmony_ci DBG_BUGON(1); 15962306a36Sopenharmony_ci return -EFSCORRUPTED; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci m->compressedblks = lo & ~Z_EROFS_LI_D0_CBLKCNT; 16262306a36Sopenharmony_ci m->delta[0] = 1; 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci } else if (i + 1 != (int)vcnt) { 16562306a36Sopenharmony_ci m->delta[0] = lo; 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * since the last lcluster in the pack is special, 17062306a36Sopenharmony_ci * of which lo saves delta[1] rather than delta[0]. 17162306a36Sopenharmony_ci * Hence, get delta[0] by the previous lcluster indirectly. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci lo = decode_compactedbits(lobits, in, 17462306a36Sopenharmony_ci encodebits * (i - 1), &type); 17562306a36Sopenharmony_ci if (type != Z_EROFS_LCLUSTER_TYPE_NONHEAD) 17662306a36Sopenharmony_ci lo = 0; 17762306a36Sopenharmony_ci else if (lo & Z_EROFS_LI_D0_CBLKCNT) 17862306a36Sopenharmony_ci lo = 1; 17962306a36Sopenharmony_ci m->delta[0] = lo + 1; 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci m->clusterofs = lo; 18362306a36Sopenharmony_ci m->delta[0] = 0; 18462306a36Sopenharmony_ci /* figout out blkaddr (pblk) for HEAD lclusters */ 18562306a36Sopenharmony_ci if (!big_pcluster) { 18662306a36Sopenharmony_ci nblk = 1; 18762306a36Sopenharmony_ci while (i > 0) { 18862306a36Sopenharmony_ci --i; 18962306a36Sopenharmony_ci lo = decode_compactedbits(lobits, in, 19062306a36Sopenharmony_ci encodebits * i, &type); 19162306a36Sopenharmony_ci if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) 19262306a36Sopenharmony_ci i -= lo; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (i >= 0) 19562306a36Sopenharmony_ci ++nblk; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } else { 19862306a36Sopenharmony_ci nblk = 0; 19962306a36Sopenharmony_ci while (i > 0) { 20062306a36Sopenharmony_ci --i; 20162306a36Sopenharmony_ci lo = decode_compactedbits(lobits, in, 20262306a36Sopenharmony_ci encodebits * i, &type); 20362306a36Sopenharmony_ci if (type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) { 20462306a36Sopenharmony_ci if (lo & Z_EROFS_LI_D0_CBLKCNT) { 20562306a36Sopenharmony_ci --i; 20662306a36Sopenharmony_ci nblk += lo & ~Z_EROFS_LI_D0_CBLKCNT; 20762306a36Sopenharmony_ci continue; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci /* bigpcluster shouldn't have plain d0 == 1 */ 21062306a36Sopenharmony_ci if (lo <= 1) { 21162306a36Sopenharmony_ci DBG_BUGON(1); 21262306a36Sopenharmony_ci return -EFSCORRUPTED; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci i -= lo - 2; 21562306a36Sopenharmony_ci continue; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci ++nblk; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci in += (vcnt << amortizedshift) - sizeof(__le32); 22162306a36Sopenharmony_ci m->pblk = le32_to_cpu(*(__le32 *)in) + nblk; 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m, 22662306a36Sopenharmony_ci unsigned long lcn, bool lookahead) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct inode *const inode = m->inode; 22962306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 23062306a36Sopenharmony_ci const erofs_off_t ebase = sizeof(struct z_erofs_map_header) + 23162306a36Sopenharmony_ci ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, 8); 23262306a36Sopenharmony_ci unsigned int totalidx = erofs_iblks(inode); 23362306a36Sopenharmony_ci unsigned int compacted_4b_initial, compacted_2b; 23462306a36Sopenharmony_ci unsigned int amortizedshift; 23562306a36Sopenharmony_ci erofs_off_t pos; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (lcn >= totalidx) 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci m->lcn = lcn; 24162306a36Sopenharmony_ci /* used to align to 32-byte (compacted_2b) alignment */ 24262306a36Sopenharmony_ci compacted_4b_initial = (32 - ebase % 32) / 4; 24362306a36Sopenharmony_ci if (compacted_4b_initial == 32 / 4) 24462306a36Sopenharmony_ci compacted_4b_initial = 0; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if ((vi->z_advise & Z_EROFS_ADVISE_COMPACTED_2B) && 24762306a36Sopenharmony_ci compacted_4b_initial < totalidx) 24862306a36Sopenharmony_ci compacted_2b = rounddown(totalidx - compacted_4b_initial, 16); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci compacted_2b = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pos = ebase; 25362306a36Sopenharmony_ci if (lcn < compacted_4b_initial) { 25462306a36Sopenharmony_ci amortizedshift = 2; 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci pos += compacted_4b_initial * 4; 25862306a36Sopenharmony_ci lcn -= compacted_4b_initial; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (lcn < compacted_2b) { 26162306a36Sopenharmony_ci amortizedshift = 1; 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci pos += compacted_2b * 2; 26562306a36Sopenharmony_ci lcn -= compacted_2b; 26662306a36Sopenharmony_ci amortizedshift = 2; 26762306a36Sopenharmony_ciout: 26862306a36Sopenharmony_ci pos += lcn * (1 << amortizedshift); 26962306a36Sopenharmony_ci m->kaddr = erofs_read_metabuf(&m->map->buf, inode->i_sb, 27062306a36Sopenharmony_ci erofs_blknr(inode->i_sb, pos), EROFS_KMAP); 27162306a36Sopenharmony_ci if (IS_ERR(m->kaddr)) 27262306a36Sopenharmony_ci return PTR_ERR(m->kaddr); 27362306a36Sopenharmony_ci return unpack_compacted_index(m, amortizedshift, pos, lookahead); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int z_erofs_load_lcluster_from_disk(struct z_erofs_maprecorder *m, 27762306a36Sopenharmony_ci unsigned int lcn, bool lookahead) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci switch (EROFS_I(m->inode)->datalayout) { 28062306a36Sopenharmony_ci case EROFS_INODE_COMPRESSED_FULL: 28162306a36Sopenharmony_ci return z_erofs_load_full_lcluster(m, lcn); 28262306a36Sopenharmony_ci case EROFS_INODE_COMPRESSED_COMPACT: 28362306a36Sopenharmony_ci return z_erofs_load_compact_lcluster(m, lcn, lookahead); 28462306a36Sopenharmony_ci default: 28562306a36Sopenharmony_ci return -EINVAL; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int z_erofs_extent_lookback(struct z_erofs_maprecorder *m, 29062306a36Sopenharmony_ci unsigned int lookback_distance) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct super_block *sb = m->inode->i_sb; 29362306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(m->inode); 29462306a36Sopenharmony_ci const unsigned int lclusterbits = vi->z_logical_clusterbits; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci while (m->lcn >= lookback_distance) { 29762306a36Sopenharmony_ci unsigned long lcn = m->lcn - lookback_distance; 29862306a36Sopenharmony_ci int err; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci err = z_erofs_load_lcluster_from_disk(m, lcn, false); 30162306a36Sopenharmony_ci if (err) 30262306a36Sopenharmony_ci return err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci switch (m->type) { 30562306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_NONHEAD: 30662306a36Sopenharmony_ci lookback_distance = m->delta[0]; 30762306a36Sopenharmony_ci if (!lookback_distance) 30862306a36Sopenharmony_ci goto err_bogus; 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_PLAIN: 31162306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD1: 31262306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD2: 31362306a36Sopenharmony_ci m->headtype = m->type; 31462306a36Sopenharmony_ci m->map->m_la = (lcn << lclusterbits) | m->clusterofs; 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci default: 31762306a36Sopenharmony_ci erofs_err(sb, "unknown type %u @ lcn %lu of nid %llu", 31862306a36Sopenharmony_ci m->type, lcn, vi->nid); 31962306a36Sopenharmony_ci DBG_BUGON(1); 32062306a36Sopenharmony_ci return -EOPNOTSUPP; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_cierr_bogus: 32462306a36Sopenharmony_ci erofs_err(sb, "bogus lookback distance %u @ lcn %lu of nid %llu", 32562306a36Sopenharmony_ci lookback_distance, m->lcn, vi->nid); 32662306a36Sopenharmony_ci DBG_BUGON(1); 32762306a36Sopenharmony_ci return -EFSCORRUPTED; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m, 33162306a36Sopenharmony_ci unsigned int initial_lcn) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct super_block *sb = m->inode->i_sb; 33462306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(m->inode); 33562306a36Sopenharmony_ci struct erofs_map_blocks *const map = m->map; 33662306a36Sopenharmony_ci const unsigned int lclusterbits = vi->z_logical_clusterbits; 33762306a36Sopenharmony_ci unsigned long lcn; 33862306a36Sopenharmony_ci int err; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci DBG_BUGON(m->type != Z_EROFS_LCLUSTER_TYPE_PLAIN && 34162306a36Sopenharmony_ci m->type != Z_EROFS_LCLUSTER_TYPE_HEAD1 && 34262306a36Sopenharmony_ci m->type != Z_EROFS_LCLUSTER_TYPE_HEAD2); 34362306a36Sopenharmony_ci DBG_BUGON(m->type != m->headtype); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (m->headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN || 34662306a36Sopenharmony_ci ((m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD1) && 34762306a36Sopenharmony_ci !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1)) || 34862306a36Sopenharmony_ci ((m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2) && 34962306a36Sopenharmony_ci !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2))) { 35062306a36Sopenharmony_ci map->m_plen = 1ULL << lclusterbits; 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci lcn = m->lcn + 1; 35462306a36Sopenharmony_ci if (m->compressedblks) 35562306a36Sopenharmony_ci goto out; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci err = z_erofs_load_lcluster_from_disk(m, lcn, false); 35862306a36Sopenharmony_ci if (err) 35962306a36Sopenharmony_ci return err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * If the 1st NONHEAD lcluster has already been handled initially w/o 36362306a36Sopenharmony_ci * valid compressedblks, which means at least it mustn't be CBLKCNT, or 36462306a36Sopenharmony_ci * an internal implemenatation error is detected. 36562306a36Sopenharmony_ci * 36662306a36Sopenharmony_ci * The following code can also handle it properly anyway, but let's 36762306a36Sopenharmony_ci * BUG_ON in the debugging mode only for developers to notice that. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci DBG_BUGON(lcn == initial_lcn && 37062306a36Sopenharmony_ci m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci switch (m->type) { 37362306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_PLAIN: 37462306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD1: 37562306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD2: 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * if the 1st NONHEAD lcluster is actually PLAIN or HEAD type 37862306a36Sopenharmony_ci * rather than CBLKCNT, it's a 1 lcluster-sized pcluster. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci m->compressedblks = 1 << (lclusterbits - sb->s_blocksize_bits); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_NONHEAD: 38362306a36Sopenharmony_ci if (m->delta[0] != 1) 38462306a36Sopenharmony_ci goto err_bonus_cblkcnt; 38562306a36Sopenharmony_ci if (m->compressedblks) 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci fallthrough; 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci erofs_err(sb, "cannot found CBLKCNT @ lcn %lu of nid %llu", lcn, 39062306a36Sopenharmony_ci vi->nid); 39162306a36Sopenharmony_ci DBG_BUGON(1); 39262306a36Sopenharmony_ci return -EFSCORRUPTED; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ciout: 39562306a36Sopenharmony_ci map->m_plen = erofs_pos(sb, m->compressedblks); 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_cierr_bonus_cblkcnt: 39862306a36Sopenharmony_ci erofs_err(sb, "bogus CBLKCNT @ lcn %lu of nid %llu", lcn, vi->nid); 39962306a36Sopenharmony_ci DBG_BUGON(1); 40062306a36Sopenharmony_ci return -EFSCORRUPTED; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct inode *inode = m->inode; 40662306a36Sopenharmony_ci struct erofs_inode *vi = EROFS_I(inode); 40762306a36Sopenharmony_ci struct erofs_map_blocks *map = m->map; 40862306a36Sopenharmony_ci unsigned int lclusterbits = vi->z_logical_clusterbits; 40962306a36Sopenharmony_ci u64 lcn = m->lcn, headlcn = map->m_la >> lclusterbits; 41062306a36Sopenharmony_ci int err; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci do { 41362306a36Sopenharmony_ci /* handle the last EOF pcluster (no next HEAD lcluster) */ 41462306a36Sopenharmony_ci if ((lcn << lclusterbits) >= inode->i_size) { 41562306a36Sopenharmony_ci map->m_llen = inode->i_size - map->m_la; 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci err = z_erofs_load_lcluster_from_disk(m, lcn, true); 42062306a36Sopenharmony_ci if (err) 42162306a36Sopenharmony_ci return err; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) { 42462306a36Sopenharmony_ci DBG_BUGON(!m->delta[1] && 42562306a36Sopenharmony_ci m->clusterofs != 1 << lclusterbits); 42662306a36Sopenharmony_ci } else if (m->type == Z_EROFS_LCLUSTER_TYPE_PLAIN || 42762306a36Sopenharmony_ci m->type == Z_EROFS_LCLUSTER_TYPE_HEAD1 || 42862306a36Sopenharmony_ci m->type == Z_EROFS_LCLUSTER_TYPE_HEAD2) { 42962306a36Sopenharmony_ci /* go on until the next HEAD lcluster */ 43062306a36Sopenharmony_ci if (lcn != headlcn) 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci m->delta[1] = 1; 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci erofs_err(inode->i_sb, "unknown type %u @ lcn %llu of nid %llu", 43562306a36Sopenharmony_ci m->type, lcn, vi->nid); 43662306a36Sopenharmony_ci DBG_BUGON(1); 43762306a36Sopenharmony_ci return -EOPNOTSUPP; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci lcn += m->delta[1]; 44062306a36Sopenharmony_ci } while (m->delta[1]); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci map->m_llen = (lcn << lclusterbits) + m->clusterofs - map->m_la; 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int z_erofs_do_map_blocks(struct inode *inode, 44762306a36Sopenharmony_ci struct erofs_map_blocks *map, int flags) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 45062306a36Sopenharmony_ci bool ztailpacking = vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER; 45162306a36Sopenharmony_ci bool fragment = vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER; 45262306a36Sopenharmony_ci struct z_erofs_maprecorder m = { 45362306a36Sopenharmony_ci .inode = inode, 45462306a36Sopenharmony_ci .map = map, 45562306a36Sopenharmony_ci }; 45662306a36Sopenharmony_ci int err = 0; 45762306a36Sopenharmony_ci unsigned int lclusterbits, endoff, afmt; 45862306a36Sopenharmony_ci unsigned long initial_lcn; 45962306a36Sopenharmony_ci unsigned long long ofs, end; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci lclusterbits = vi->z_logical_clusterbits; 46262306a36Sopenharmony_ci ofs = flags & EROFS_GET_BLOCKS_FINDTAIL ? inode->i_size - 1 : map->m_la; 46362306a36Sopenharmony_ci initial_lcn = ofs >> lclusterbits; 46462306a36Sopenharmony_ci endoff = ofs & ((1 << lclusterbits) - 1); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = z_erofs_load_lcluster_from_disk(&m, initial_lcn, false); 46762306a36Sopenharmony_ci if (err) 46862306a36Sopenharmony_ci goto unmap_out; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (ztailpacking && (flags & EROFS_GET_BLOCKS_FINDTAIL)) 47162306a36Sopenharmony_ci vi->z_idataoff = m.nextpackoff; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_ENCODED; 47462306a36Sopenharmony_ci end = (m.lcn + 1ULL) << lclusterbits; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci switch (m.type) { 47762306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_PLAIN: 47862306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD1: 47962306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_HEAD2: 48062306a36Sopenharmony_ci if (endoff >= m.clusterofs) { 48162306a36Sopenharmony_ci m.headtype = m.type; 48262306a36Sopenharmony_ci map->m_la = (m.lcn << lclusterbits) | m.clusterofs; 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * For ztailpacking files, in order to inline data more 48562306a36Sopenharmony_ci * effectively, special EOF lclusters are now supported 48662306a36Sopenharmony_ci * which can have three parts at most. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci if (ztailpacking && end > inode->i_size) 48962306a36Sopenharmony_ci end = inode->i_size; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci /* m.lcn should be >= 1 if endoff < m.clusterofs */ 49362306a36Sopenharmony_ci if (!m.lcn) { 49462306a36Sopenharmony_ci erofs_err(inode->i_sb, 49562306a36Sopenharmony_ci "invalid logical cluster 0 at nid %llu", 49662306a36Sopenharmony_ci vi->nid); 49762306a36Sopenharmony_ci err = -EFSCORRUPTED; 49862306a36Sopenharmony_ci goto unmap_out; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci end = (m.lcn << lclusterbits) | m.clusterofs; 50162306a36Sopenharmony_ci map->m_flags |= EROFS_MAP_FULL_MAPPED; 50262306a36Sopenharmony_ci m.delta[0] = 1; 50362306a36Sopenharmony_ci fallthrough; 50462306a36Sopenharmony_ci case Z_EROFS_LCLUSTER_TYPE_NONHEAD: 50562306a36Sopenharmony_ci /* get the corresponding first chunk */ 50662306a36Sopenharmony_ci err = z_erofs_extent_lookback(&m, m.delta[0]); 50762306a36Sopenharmony_ci if (err) 50862306a36Sopenharmony_ci goto unmap_out; 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci default: 51162306a36Sopenharmony_ci erofs_err(inode->i_sb, 51262306a36Sopenharmony_ci "unknown type %u @ offset %llu of nid %llu", 51362306a36Sopenharmony_ci m.type, ofs, vi->nid); 51462306a36Sopenharmony_ci err = -EOPNOTSUPP; 51562306a36Sopenharmony_ci goto unmap_out; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci if (m.partialref) 51862306a36Sopenharmony_ci map->m_flags |= EROFS_MAP_PARTIAL_REF; 51962306a36Sopenharmony_ci map->m_llen = end - map->m_la; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (flags & EROFS_GET_BLOCKS_FINDTAIL) { 52262306a36Sopenharmony_ci vi->z_tailextent_headlcn = m.lcn; 52362306a36Sopenharmony_ci /* for non-compact indexes, fragmentoff is 64 bits */ 52462306a36Sopenharmony_ci if (fragment && vi->datalayout == EROFS_INODE_COMPRESSED_FULL) 52562306a36Sopenharmony_ci vi->z_fragmentoff |= (u64)m.pblk << 32; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci if (ztailpacking && m.lcn == vi->z_tailextent_headlcn) { 52862306a36Sopenharmony_ci map->m_flags |= EROFS_MAP_META; 52962306a36Sopenharmony_ci map->m_pa = vi->z_idataoff; 53062306a36Sopenharmony_ci map->m_plen = vi->z_idata_size; 53162306a36Sopenharmony_ci } else if (fragment && m.lcn == vi->z_tailextent_headlcn) { 53262306a36Sopenharmony_ci map->m_flags |= EROFS_MAP_FRAGMENT; 53362306a36Sopenharmony_ci } else { 53462306a36Sopenharmony_ci map->m_pa = erofs_pos(inode->i_sb, m.pblk); 53562306a36Sopenharmony_ci err = z_erofs_get_extent_compressedlen(&m, initial_lcn); 53662306a36Sopenharmony_ci if (err) 53762306a36Sopenharmony_ci goto unmap_out; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (m.headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN) { 54162306a36Sopenharmony_ci if (map->m_llen > map->m_plen) { 54262306a36Sopenharmony_ci DBG_BUGON(1); 54362306a36Sopenharmony_ci err = -EFSCORRUPTED; 54462306a36Sopenharmony_ci goto unmap_out; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci afmt = vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER ? 54762306a36Sopenharmony_ci Z_EROFS_COMPRESSION_INTERLACED : 54862306a36Sopenharmony_ci Z_EROFS_COMPRESSION_SHIFTED; 54962306a36Sopenharmony_ci } else { 55062306a36Sopenharmony_ci afmt = m.headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2 ? 55162306a36Sopenharmony_ci vi->z_algorithmtype[1] : vi->z_algorithmtype[0]; 55262306a36Sopenharmony_ci if (!(EROFS_I_SB(inode)->available_compr_algs & (1 << afmt))) { 55362306a36Sopenharmony_ci erofs_err(inode->i_sb, "inconsistent algorithmtype %u for nid %llu", 55462306a36Sopenharmony_ci afmt, vi->nid); 55562306a36Sopenharmony_ci err = -EFSCORRUPTED; 55662306a36Sopenharmony_ci goto unmap_out; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci map->m_algorithmformat = afmt; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if ((flags & EROFS_GET_BLOCKS_FIEMAP) || 56262306a36Sopenharmony_ci ((flags & EROFS_GET_BLOCKS_READMORE) && 56362306a36Sopenharmony_ci (map->m_algorithmformat == Z_EROFS_COMPRESSION_LZMA || 56462306a36Sopenharmony_ci map->m_algorithmformat == Z_EROFS_COMPRESSION_DEFLATE) && 56562306a36Sopenharmony_ci map->m_llen >= i_blocksize(inode))) { 56662306a36Sopenharmony_ci err = z_erofs_get_extent_decompressedlen(&m); 56762306a36Sopenharmony_ci if (!err) 56862306a36Sopenharmony_ci map->m_flags |= EROFS_MAP_FULL_MAPPED; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ciunmap_out: 57262306a36Sopenharmony_ci erofs_unmap_metabuf(&m.map->buf); 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int z_erofs_fill_inode_lazy(struct inode *inode) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 57962306a36Sopenharmony_ci struct super_block *const sb = inode->i_sb; 58062306a36Sopenharmony_ci int err, headnr; 58162306a36Sopenharmony_ci erofs_off_t pos; 58262306a36Sopenharmony_ci struct erofs_buf buf = __EROFS_BUF_INITIALIZER; 58362306a36Sopenharmony_ci void *kaddr; 58462306a36Sopenharmony_ci struct z_erofs_map_header *h; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (test_bit(EROFS_I_Z_INITED_BIT, &vi->flags)) { 58762306a36Sopenharmony_ci /* 58862306a36Sopenharmony_ci * paired with smp_mb() at the end of the function to ensure 58962306a36Sopenharmony_ci * fields will only be observed after the bit is set. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci smp_mb(); 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (wait_on_bit_lock(&vi->flags, EROFS_I_BL_Z_BIT, TASK_KILLABLE)) 59662306a36Sopenharmony_ci return -ERESTARTSYS; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci err = 0; 59962306a36Sopenharmony_ci if (test_bit(EROFS_I_Z_INITED_BIT, &vi->flags)) 60062306a36Sopenharmony_ci goto out_unlock; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pos = ALIGN(erofs_iloc(inode) + vi->inode_isize + vi->xattr_isize, 8); 60362306a36Sopenharmony_ci kaddr = erofs_read_metabuf(&buf, sb, erofs_blknr(sb, pos), EROFS_KMAP); 60462306a36Sopenharmony_ci if (IS_ERR(kaddr)) { 60562306a36Sopenharmony_ci err = PTR_ERR(kaddr); 60662306a36Sopenharmony_ci goto out_unlock; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci h = kaddr + erofs_blkoff(sb, pos); 61062306a36Sopenharmony_ci /* 61162306a36Sopenharmony_ci * if the highest bit of the 8-byte map header is set, the whole file 61262306a36Sopenharmony_ci * is stored in the packed inode. The rest bits keeps z_fragmentoff. 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci if (h->h_clusterbits >> Z_EROFS_FRAGMENT_INODE_BIT) { 61562306a36Sopenharmony_ci vi->z_advise = Z_EROFS_ADVISE_FRAGMENT_PCLUSTER; 61662306a36Sopenharmony_ci vi->z_fragmentoff = le64_to_cpu(*(__le64 *)h) ^ (1ULL << 63); 61762306a36Sopenharmony_ci vi->z_tailextent_headlcn = 0; 61862306a36Sopenharmony_ci goto done; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci vi->z_advise = le16_to_cpu(h->h_advise); 62162306a36Sopenharmony_ci vi->z_algorithmtype[0] = h->h_algorithmtype & 15; 62262306a36Sopenharmony_ci vi->z_algorithmtype[1] = h->h_algorithmtype >> 4; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci headnr = 0; 62562306a36Sopenharmony_ci if (vi->z_algorithmtype[0] >= Z_EROFS_COMPRESSION_MAX || 62662306a36Sopenharmony_ci vi->z_algorithmtype[++headnr] >= Z_EROFS_COMPRESSION_MAX) { 62762306a36Sopenharmony_ci erofs_err(sb, "unknown HEAD%u format %u for nid %llu, please upgrade kernel", 62862306a36Sopenharmony_ci headnr + 1, vi->z_algorithmtype[headnr], vi->nid); 62962306a36Sopenharmony_ci err = -EOPNOTSUPP; 63062306a36Sopenharmony_ci goto out_put_metabuf; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci vi->z_logical_clusterbits = sb->s_blocksize_bits + (h->h_clusterbits & 7); 63462306a36Sopenharmony_ci if (!erofs_sb_has_big_pcluster(EROFS_SB(sb)) && 63562306a36Sopenharmony_ci vi->z_advise & (Z_EROFS_ADVISE_BIG_PCLUSTER_1 | 63662306a36Sopenharmony_ci Z_EROFS_ADVISE_BIG_PCLUSTER_2)) { 63762306a36Sopenharmony_ci erofs_err(sb, "per-inode big pcluster without sb feature for nid %llu", 63862306a36Sopenharmony_ci vi->nid); 63962306a36Sopenharmony_ci err = -EFSCORRUPTED; 64062306a36Sopenharmony_ci goto out_put_metabuf; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci if (vi->datalayout == EROFS_INODE_COMPRESSED_COMPACT && 64362306a36Sopenharmony_ci !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) ^ 64462306a36Sopenharmony_ci !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2)) { 64562306a36Sopenharmony_ci erofs_err(sb, "big pcluster head1/2 of compact indexes should be consistent for nid %llu", 64662306a36Sopenharmony_ci vi->nid); 64762306a36Sopenharmony_ci err = -EFSCORRUPTED; 64862306a36Sopenharmony_ci goto out_put_metabuf; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) { 65262306a36Sopenharmony_ci struct erofs_map_blocks map = { 65362306a36Sopenharmony_ci .buf = __EROFS_BUF_INITIALIZER 65462306a36Sopenharmony_ci }; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci vi->z_idata_size = le16_to_cpu(h->h_idata_size); 65762306a36Sopenharmony_ci err = z_erofs_do_map_blocks(inode, &map, 65862306a36Sopenharmony_ci EROFS_GET_BLOCKS_FINDTAIL); 65962306a36Sopenharmony_ci erofs_put_metabuf(&map.buf); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!map.m_plen || 66262306a36Sopenharmony_ci erofs_blkoff(sb, map.m_pa) + map.m_plen > sb->s_blocksize) { 66362306a36Sopenharmony_ci erofs_err(sb, "invalid tail-packing pclustersize %llu", 66462306a36Sopenharmony_ci map.m_plen); 66562306a36Sopenharmony_ci err = -EFSCORRUPTED; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (err < 0) 66862306a36Sopenharmony_ci goto out_put_metabuf; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER && 67262306a36Sopenharmony_ci !(h->h_clusterbits >> Z_EROFS_FRAGMENT_INODE_BIT)) { 67362306a36Sopenharmony_ci struct erofs_map_blocks map = { 67462306a36Sopenharmony_ci .buf = __EROFS_BUF_INITIALIZER 67562306a36Sopenharmony_ci }; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci vi->z_fragmentoff = le32_to_cpu(h->h_fragmentoff); 67862306a36Sopenharmony_ci err = z_erofs_do_map_blocks(inode, &map, 67962306a36Sopenharmony_ci EROFS_GET_BLOCKS_FINDTAIL); 68062306a36Sopenharmony_ci erofs_put_metabuf(&map.buf); 68162306a36Sopenharmony_ci if (err < 0) 68262306a36Sopenharmony_ci goto out_put_metabuf; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_cidone: 68562306a36Sopenharmony_ci /* paired with smp_mb() at the beginning of the function */ 68662306a36Sopenharmony_ci smp_mb(); 68762306a36Sopenharmony_ci set_bit(EROFS_I_Z_INITED_BIT, &vi->flags); 68862306a36Sopenharmony_ciout_put_metabuf: 68962306a36Sopenharmony_ci erofs_put_metabuf(&buf); 69062306a36Sopenharmony_ciout_unlock: 69162306a36Sopenharmony_ci clear_and_wake_up_bit(EROFS_I_BL_Z_BIT, &vi->flags); 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ciint z_erofs_map_blocks_iter(struct inode *inode, struct erofs_map_blocks *map, 69662306a36Sopenharmony_ci int flags) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct erofs_inode *const vi = EROFS_I(inode); 69962306a36Sopenharmony_ci int err = 0; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci trace_z_erofs_map_blocks_iter_enter(inode, map, flags); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* when trying to read beyond EOF, leave it unmapped */ 70462306a36Sopenharmony_ci if (map->m_la >= inode->i_size) { 70562306a36Sopenharmony_ci map->m_llen = map->m_la + 1 - inode->i_size; 70662306a36Sopenharmony_ci map->m_la = inode->i_size; 70762306a36Sopenharmony_ci map->m_flags = 0; 70862306a36Sopenharmony_ci goto out; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci err = z_erofs_fill_inode_lazy(inode); 71262306a36Sopenharmony_ci if (err) 71362306a36Sopenharmony_ci goto out; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if ((vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) && 71662306a36Sopenharmony_ci !vi->z_tailextent_headlcn) { 71762306a36Sopenharmony_ci map->m_la = 0; 71862306a36Sopenharmony_ci map->m_llen = inode->i_size; 71962306a36Sopenharmony_ci map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_FULL_MAPPED | 72062306a36Sopenharmony_ci EROFS_MAP_FRAGMENT; 72162306a36Sopenharmony_ci goto out; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci err = z_erofs_do_map_blocks(inode, map, flags); 72562306a36Sopenharmony_ciout: 72662306a36Sopenharmony_ci trace_z_erofs_map_blocks_iter_exit(inode, map, flags, err); 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset, 73162306a36Sopenharmony_ci loff_t length, unsigned int flags, 73262306a36Sopenharmony_ci struct iomap *iomap, struct iomap *srcmap) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci struct erofs_map_blocks map = { .m_la = offset }; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ret = z_erofs_map_blocks_iter(inode, &map, EROFS_GET_BLOCKS_FIEMAP); 73862306a36Sopenharmony_ci erofs_put_metabuf(&map.buf); 73962306a36Sopenharmony_ci if (ret < 0) 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci iomap->bdev = inode->i_sb->s_bdev; 74362306a36Sopenharmony_ci iomap->offset = map.m_la; 74462306a36Sopenharmony_ci iomap->length = map.m_llen; 74562306a36Sopenharmony_ci if (map.m_flags & EROFS_MAP_MAPPED) { 74662306a36Sopenharmony_ci iomap->type = IOMAP_MAPPED; 74762306a36Sopenharmony_ci iomap->addr = map.m_flags & EROFS_MAP_FRAGMENT ? 74862306a36Sopenharmony_ci IOMAP_NULL_ADDR : map.m_pa; 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci iomap->type = IOMAP_HOLE; 75162306a36Sopenharmony_ci iomap->addr = IOMAP_NULL_ADDR; 75262306a36Sopenharmony_ci /* 75362306a36Sopenharmony_ci * No strict rule on how to describe extents for post EOF, yet 75462306a36Sopenharmony_ci * we need to do like below. Otherwise, iomap itself will get 75562306a36Sopenharmony_ci * into an endless loop on post EOF. 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Calculate the effective offset by subtracting extent start 75862306a36Sopenharmony_ci * (map.m_la) from the requested offset, and add it to length. 75962306a36Sopenharmony_ci * (NB: offset >= map.m_la always) 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci if (iomap->offset >= inode->i_size) 76262306a36Sopenharmony_ci iomap->length = length + offset - map.m_la; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci iomap->flags = 0; 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciconst struct iomap_ops z_erofs_iomap_report_ops = { 76962306a36Sopenharmony_ci .iomap_begin = z_erofs_iomap_begin_report, 77062306a36Sopenharmony_ci}; 771