[PATCH v2 09/10] erofs-utils: lib: implement encoded extent metadata

Gao Xiang hsiangkao at linux.alibaba.com
Thu Apr 10 02:32:58 AEST 2025


Source kernel commit: efb2aef569b35b415c232c4e9fdecd0e540e1f60
Source kernel commit: 1d191b4ca51d73699cb127386b95ac152af2b930

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 fsck/main.c              |   2 +-
 include/erofs/internal.h |   3 +-
 lib/compress.c           |  14 +--
 lib/zmap.c               | 178 ++++++++++++++++++++++++++++++++++-----
 4 files changed, 166 insertions(+), 31 deletions(-)

diff --git a/fsck/main.c b/fsck/main.c
index f7e33c0..cb4758b 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -572,7 +572,7 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd)
 		}
 
 		if (map.m_plen > Z_EROFS_PCLUSTER_MAX_SIZE) {
-			if (compressed) {
+			if (compressed && !(map.m_flags & EROFS_MAP_FRAGMENT)) {
 				erofs_err("invalid pcluster size %" PRIu64 " @ offset %" PRIu64 " of nid %" PRIu64,
 					  map.m_plen, map.m_la,
 					  inode->nid | 0ULL);
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 94bca2d..676f63d 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -262,11 +262,12 @@ struct erofs_inode {
 		struct {
 			uint16_t z_advise;
 			uint8_t  z_algorithmtype[2];
-			uint8_t  z_logical_clusterbits;
+			uint8_t  z_lclusterbits;
 			uint8_t  z_physical_clusterblks;
 			union {
 				uint64_t z_tailextent_headlcn;
 				erofs_off_t fragment_size;
+				u64		z_extents;
 			};
 			union {
 				erofs_off_t	fragmentoff;
diff --git a/lib/compress.c b/lib/compress.c
index 30bcdd4..f81539a 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -272,7 +272,7 @@ static void z_erofs_commit_extent(struct z_erofs_compress_sctx *ctx,
 static int z_erofs_compress_dedupe(struct z_erofs_compress_sctx *ctx)
 {
 	struct erofs_inode *inode = ctx->ictx->inode;
-	const unsigned int lclustermask = (1 << inode->z_logical_clusterbits) - 1;
+	const unsigned int lclustermask = (1 << inode->z_lclusterbits) - 1;
 	struct erofs_sb_info *sbi = inode->sbi;
 	struct z_erofs_extent_item *ei = ctx->pivot;
 
@@ -405,7 +405,7 @@ static int write_uncompressed_extents(struct z_erofs_compress_sctx *ctx,
 				      char *dst)
 {
 	struct erofs_inode *inode = ctx->ictx->inode;
-	unsigned int lclustersize = 1 << inode->z_logical_clusterbits;
+	unsigned int lclustersize = 1 << inode->z_lclusterbits;
 	struct z_erofs_extent_item *ei;
 	int count;
 
@@ -875,7 +875,7 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode,
 	const unsigned int totalidx = (legacymetasize -
 			Z_EROFS_LEGACY_MAP_HEADER_SIZE) /
 				sizeof(struct z_erofs_lcluster_index);
-	const unsigned int logical_clusterbits = inode->z_logical_clusterbits;
+	const unsigned int logical_clusterbits = inode->z_lclusterbits;
 	u8 *out, *in;
 	struct z_erofs_compressindex_vec cv[16];
 	struct erofs_sb_info *sbi = inode->sbi;
@@ -984,7 +984,7 @@ static void z_erofs_write_mapheader(struct erofs_inode *inode,
 		.h_algorithmtype = inode->z_algorithmtype[1] << 4 |
 				   inode->z_algorithmtype[0],
 		/* lclustersize */
-		.h_clusterbits = inode->z_logical_clusterbits - sbi->blkszbits,
+		.h_clusterbits = inode->z_lclusterbits - sbi->blkszbits,
 	};
 
 	if (inode->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)
@@ -1011,8 +1011,8 @@ static void *z_erofs_write_indexes(struct z_erofs_compress_ictx *ctx)
 	if (inode->fragment_size && inode->fragmentoff >> 32) {
 		inode->datalayout = EROFS_INODE_COMPRESSED_FULL;
 	} else if (!cfg.c_legacy_compress && !ctx->dedupe &&
-	    inode->z_logical_clusterbits <= 14) {
-		if (inode->z_logical_clusterbits <= 12)
+	    inode->z_lclusterbits <= 14) {
+		if (inode->z_lclusterbits <= 12)
 			inode->z_advise |= Z_EROFS_ADVISE_COMPACTED_2B;
 		inode->datalayout = EROFS_INODE_COMPRESSED_COMPACT;
 	} else {
@@ -1584,7 +1584,7 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos)
 
 	/* initialize per-file compression setting */
 	inode->z_advise = 0;
-	inode->z_logical_clusterbits = sbi->blkszbits;
+	inode->z_lclusterbits = sbi->blkszbits;
 	if (cfg.c_fragments && !cfg.c_dedupe)
 		inode->z_advise |= Z_EROFS_ADVISE_INTERLACED_PCLUSTER;
 
diff --git a/lib/zmap.c b/lib/zmap.c
index 8383385..99f4088 100644
--- a/lib/zmap.c
+++ b/lib/zmap.c
@@ -51,7 +51,7 @@ static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m,
 	advise = le16_to_cpu(di->di_advise);
 	m->type = advise & Z_EROFS_LI_LCLUSTER_TYPE_MASK;
 	if (m->type == Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
-		m->clusterofs = 1 << vi->z_logical_clusterbits;
+		m->clusterofs = 1 << vi->z_lclusterbits;
 		m->delta[0] = le16_to_cpu(di->di_u.delta[0]);
 		if (m->delta[0] & Z_EROFS_LI_D0_CBLKCNT) {
 			if (!(vi->z_advise & (Z_EROFS_ADVISE_BIG_PCLUSTER_1 |
@@ -66,7 +66,7 @@ static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m,
 	} else {
 		m->partialref = !!(advise & Z_EROFS_LI_PARTIAL_REF);
 		m->clusterofs = le16_to_cpu(di->di_clusterofs);
-		if (m->clusterofs >= 1 << vi->z_logical_clusterbits) {
+		if (m->clusterofs >= 1 << vi->z_lclusterbits) {
 			DBG_BUGON(1);
 			return -EFSCORRUPTED;
 		}
@@ -115,7 +115,7 @@ static int z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m,
 	struct erofs_sb_info *sbi = vi->sbi;
 	const erofs_off_t ebase = sizeof(struct z_erofs_map_header) +
 		round_up(erofs_iloc(vi) + vi->inode_isize + vi->xattr_isize, 8);
-	const unsigned int lclusterbits = vi->z_logical_clusterbits;
+	const unsigned int lclusterbits = vi->z_lclusterbits;
 	const unsigned int totalidx = BLK_ROUND_UP(sbi, vi->i_size);
 	unsigned int compacted_4b_initial, compacted_2b, amortizedshift;
 	unsigned int vcnt, base, lo, lobits, encodebits, nblk, eofs;
@@ -272,7 +272,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
 				   unsigned int lookback_distance)
 {
 	struct erofs_inode *const vi = m->inode;
-	const unsigned int lclusterbits = vi->z_logical_clusterbits;
+	const unsigned int lclusterbits = vi->z_lclusterbits;
 
 	while (m->lcn >= lookback_distance) {
 		unsigned long lcn = m->lcn - lookback_distance;
@@ -320,7 +320,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m,
 	if ((m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD1 && !bigpcl1) ||
 	    ((m->headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN ||
 	      m->headtype == Z_EROFS_LCLUSTER_TYPE_HEAD2) && !bigpcl2) ||
-	    (lcn << vi->z_logical_clusterbits) >= vi->i_size)
+	    (lcn << vi->z_lclusterbits) >= vi->i_size)
 		m->compressedblks = 1;
 
 	if (m->compressedblks)
@@ -371,7 +371,7 @@ static int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
 {
 	struct erofs_inode *const vi = m->inode;
 	struct erofs_map_blocks *map = m->map;
-	unsigned int lclusterbits = vi->z_logical_clusterbits;
+	unsigned int lclusterbits = vi->z_lclusterbits;
 	u64 lcn = m->lcn, headlcn = map->m_la >> lclusterbits;
 	int err;
 
@@ -409,25 +409,33 @@ static int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
 	return 0;
 }
 
-static int z_erofs_do_map_blocks(struct erofs_inode *vi,
+static int z_erofs_map_blocks_fo(struct erofs_inode *vi,
 				 struct erofs_map_blocks *map,
 				 int flags)
 {
 	struct erofs_sb_info *sbi = vi->sbi;
 	bool fragment = vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER;
 	bool ztailpacking = vi->z_idata_size;
+	unsigned int lclusterbits = vi->z_lclusterbits;
 	struct z_erofs_maprecorder m = {
 		.inode = vi,
 		.map = map,
 		.kaddr = map->mpage,
 	};
 	int err = 0;
-	unsigned int lclusterbits, endoff, afmt;
+	unsigned int endoff, afmt;
 	unsigned long initial_lcn;
 	unsigned long long ofs, end;
 
-	lclusterbits = vi->z_logical_clusterbits;
 	ofs = flags & EROFS_GET_BLOCKS_FINDTAIL ? vi->i_size - 1 : map->m_la;
+	if (fragment && !(flags & EROFS_GET_BLOCKS_FINDTAIL) &&
+	    !vi->z_tailextent_headlcn) {
+		map->m_la = 0;
+		map->m_llen = vi->i_size;
+		map->m_flags = EROFS_MAP_MAPPED |
+			EROFS_MAP_FULL_MAPPED | EROFS_MAP_FRAGMENT;
+		return 0;
+	}
 	initial_lcn = ofs >> lclusterbits;
 	endoff = ofs & ((1 << lclusterbits) - 1);
 
@@ -541,6 +549,130 @@ out:
 	return err;
 }
 
+static int z_erofs_map_blocks_ext(struct erofs_inode *vi,
+				  struct erofs_map_blocks *map, int flags)
+{
+	struct erofs_sb_info *sbi = vi->sbi;
+	bool interlaced = vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER;
+	unsigned int recsz = z_erofs_extent_recsize(vi->z_advise);
+	erofs_off_t pos = round_up(Z_EROFS_MAP_HEADER_END(erofs_iloc(vi) +
+				   vi->inode_isize + vi->xattr_isize), recsz);
+	erofs_off_t lend = vi->i_size;
+	erofs_off_t l, r, mid, pa, la, lstart;
+	erofs_blk_t eblk;
+	struct z_erofs_extent *ext;
+	unsigned int fmt;
+	bool last;
+	int err;
+
+	map->m_flags = 0;
+	if (recsz <= offsetof(struct z_erofs_extent, pstart_hi)) {
+		if (recsz <= offsetof(struct z_erofs_extent, pstart_lo)) {
+			eblk = erofs_blknr(sbi, pos);
+			if (map->index != eblk) {
+				err = erofs_blk_read(sbi, 0, map->mpage, eblk, 1);
+				if (err < 0)
+					return err;
+				map->index = eblk;
+			}
+			ext = (void *)(map->mpage + erofs_blkoff(sbi, pos));
+			pa = le64_to_cpu(*(__le64 *)ext);
+			pos += sizeof(__le64);
+			lstart = 0;
+		} else {
+			lstart = map->m_la >> vi->z_lclusterbits;
+			pa = EROFS_NULL_ADDR;
+		}
+
+		for (; lstart <= map->m_la; lstart += 1 << vi->z_lclusterbits) {
+			eblk = erofs_blknr(sbi, pos);
+			if (map->index != eblk) {
+				err = erofs_blk_read(sbi, 0, map->mpage, eblk, 1);
+				if (err < 0)
+					return err;
+				map->index = eblk;
+			}
+			ext = (void *)(map->mpage + erofs_blkoff(sbi, pos));
+			map->m_plen = le32_to_cpu(ext->plen);
+			if (pa != EROFS_NULL_ADDR) {
+				map->m_pa = pa;
+				pa += map->m_plen & Z_EROFS_EXTENT_PLEN_MASK;
+			} else {
+				map->m_pa = le32_to_cpu(ext->pstart_lo);
+			}
+			pos += recsz;
+		}
+		last = (lstart >= round_up(lend, 1 << vi->z_lclusterbits));
+		lend = min(lstart, lend);
+		lstart -= 1 << vi->z_lclusterbits;
+	} else {
+		lstart = lend;
+		for (l = 0, r = vi->z_extents; l < r; ) {
+			mid = l + (r - l) / 2;
+			eblk = erofs_blknr(sbi, pos + mid * recsz);
+			if (map->index != eblk) {
+				err = erofs_blk_read(sbi, 0, map->mpage, eblk, 1);
+				if (err < 0)
+					return err;
+				map->index = eblk;
+			}
+			ext = (void *)(map->mpage + erofs_blkoff(sbi, pos + mid * recsz));
+			la = le32_to_cpu(ext->lstart_lo);
+			pa = le32_to_cpu(ext->pstart_lo) |
+				(u64)le32_to_cpu(ext->pstart_hi) << 32;
+			if (recsz > offsetof(struct z_erofs_extent, lstart_hi))
+				la |= (u64)le32_to_cpu(ext->lstart_hi) << 32;
+
+			if (la > map->m_la) {
+				r = mid;
+				lend = la;
+			} else {
+				l = mid + 1;
+				if (map->m_la == la)
+					r = min(l + 1, r);
+				lstart = la;
+				map->m_plen = le32_to_cpu(ext->plen);
+				map->m_pa = pa;
+			}
+		}
+		last = (l >= vi->z_extents);
+	}
+
+	if (lstart < lend) {
+		map->m_la = lstart;
+		if (last && (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)) {
+			map->m_flags |= EROFS_MAP_MAPPED | EROFS_MAP_FRAGMENT;
+			vi->z_fragmentoff = map->m_plen;
+			if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
+				vi->z_fragmentoff |= map->m_pa << 32;
+		} else if (map->m_plen) {
+			map->m_flags |= EROFS_MAP_MAPPED |
+				EROFS_MAP_FULL_MAPPED | EROFS_MAP_ENCODED;
+			fmt = map->m_plen >> Z_EROFS_EXTENT_PLEN_FMT_BIT;
+			if (fmt)
+				map->m_algorithmformat = fmt - 1;
+			else if (interlaced && !erofs_blkoff(sbi, map->m_pa))
+				map->m_algorithmformat =
+					Z_EROFS_COMPRESSION_INTERLACED;
+			else
+				map->m_algorithmformat =
+					Z_EROFS_COMPRESSION_SHIFTED;
+			if (map->m_plen & Z_EROFS_EXTENT_PLEN_PARTIAL)
+				map->m_flags |= EROFS_MAP_PARTIAL_REF;
+			map->m_plen &= Z_EROFS_EXTENT_PLEN_MASK;
+		}
+	}
+	map->m_llen = lend - map->m_la;
+	if (!last && map->m_llen < erofs_blksiz(sbi)) {
+		erofs_err("extent too small %llu @ offset %llu of nid %llu",
+			  map->m_llen, map->m_la, vi->nid);
+		DBG_BUGON(1);
+		return -EFSCORRUPTED;
+	}
+	return 0;
+}
+
+
 static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 {
 	erofs_off_t pos;
@@ -566,10 +698,17 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 		vi->z_advise = Z_EROFS_ADVISE_FRAGMENT_PCLUSTER;
 		vi->fragmentoff = le64_to_cpu(*(__le64 *)h) ^ (1ULL << 63);
 		vi->z_tailextent_headlcn = 0;
-		goto out;
+		goto done;
 	}
 
 	vi->z_advise = le16_to_cpu(h->h_advise);
+	vi->z_lclusterbits = sbi->blkszbits + (h->h_clusterbits & 15);
+	if (vi->datalayout == EROFS_INODE_COMPRESSED_FULL &&
+	    (vi->z_advise & Z_EROFS_ADVISE_EXTENTS)) {
+		vi->z_extents = le32_to_cpu(h->h_extents_lo) |
+			((u64)le16_to_cpu(h->h_extents_hi) << 32);
+		goto done;
+	}
 	vi->z_algorithmtype[0] = h->h_algorithmtype & 15;
 	vi->z_algorithmtype[1] = h->h_algorithmtype >> 4;
 	if (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)
@@ -585,7 +724,6 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 		return -EOPNOTSUPP;
 	}
 
-	vi->z_logical_clusterbits = sbi->blkszbits + (h->h_clusterbits & 7);
 	if (vi->datalayout == EROFS_INODE_COMPRESSED_COMPACT &&
 	    !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_1) ^
 	    !(vi->z_advise & Z_EROFS_ADVISE_BIG_PCLUSTER_2)) {
@@ -598,12 +736,12 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
 	    (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)) {
 		struct erofs_map_blocks map = { .index = UINT_MAX };
 
-		err = z_erofs_do_map_blocks(vi, &map,
+		err = z_erofs_map_blocks_fo(vi, &map,
 					    EROFS_GET_BLOCKS_FINDTAIL);
 		if (err < 0)
 			return err;
 	}
-out:
+done:
 	erofs_atomic_set_bit(EROFS_I_Z_INITED_BIT, &vi->flags);
 	return 0;
 }
@@ -620,15 +758,11 @@ int z_erofs_map_blocks_iter(struct erofs_inode *vi,
 	} else {
 		err = z_erofs_fill_inode_lazy(vi);
 		if (!err) {
-			if ((vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER) &&
-			    !vi->z_tailextent_headlcn) {
-				map->m_la = 0;
-				map->m_llen = vi->i_size;
-				map->m_flags = EROFS_MAP_MAPPED |
-					EROFS_MAP_FULL_MAPPED | EROFS_MAP_FRAGMENT;
-			} else {
-				err = z_erofs_do_map_blocks(vi, map, flags);
-			}
+			if (vi->datalayout == EROFS_INODE_COMPRESSED_FULL &&
+			    (vi->z_advise & Z_EROFS_ADVISE_EXTENTS))
+				err = z_erofs_map_blocks_ext(vi, map, flags);
+			else
+				err = z_erofs_map_blocks_fo(vi, map, flags);
 		}
 		if (!err && (map->m_flags & EROFS_MAP_ENCODED) &&
 		    __erofs_unlikely(map->m_plen > Z_EROFS_PCLUSTER_MAX_SIZE ||
-- 
2.43.5



More information about the Linux-erofs mailing list