[PATCH 2/2] erofs-utils: lib: add gzran virtual file interfaces

Gao Xiang hsiangkao at linux.alibaba.com
Wed Sep 17 18:16:53 AEST 2025


Note that support for multiple gzip streams (e.g., stargz/estargz) will
be added later.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 lib/gzran.c          | 176 +++++++++++++++++++++++++++++++++++++++++++
 lib/liberofs_gzran.h |   2 +
 2 files changed, 178 insertions(+)

diff --git a/lib/gzran.c b/lib/gzran.c
index ce2759b..6a6fddc 100644
--- a/lib/gzran.c
+++ b/lib/gzran.c
@@ -194,6 +194,176 @@ int erofs_gzran_builder_final(struct erofs_gzran_builder *gb)
 	free(gb);
 	return 0;
 }
+
+struct erofs_gzran_iostream {
+	struct erofs_vfile *vin;
+	struct erofs_gzran_cutpoint *cp;
+	u32 entries;
+	u32 span_size;
+};
+
+static void erofs_gzran_ios_vfclose(struct erofs_vfile *vf)
+{
+	struct erofs_gzran_iostream *ios =
+		(struct erofs_gzran_iostream *)vf->payload;
+	free(ios->cp);
+	free(vf);
+}
+
+static ssize_t erofs_gzran_ios_vfpread(struct erofs_vfile *vf, void *buf, size_t len, u64 offset)
+{
+	struct erofs_gzran_iostream *ios =
+		(struct erofs_gzran_iostream *)vf->payload;
+	struct erofs_gzran_cutpoint *cp = ios->cp;
+	u8 src[1 << 14], discard[EROFS_GZRAN_WINSIZE];
+	unsigned int bits;
+	bool skip = true;
+	u64 inpos;
+	z_stream strm;
+	int ret;
+
+	while (cp < ios->cp + ios->entries - 1 && cp[1].outpos <= offset)
+		++cp;
+
+	strm.zalloc = Z_NULL;
+	strm.zfree = Z_NULL;
+	strm.opaque = Z_NULL;
+	strm.avail_in = 0;
+	strm.next_in = Z_NULL;
+	ret = inflateInit2(&strm, -15);		/* raw inflate */
+	if (ret != Z_OK)
+		return -EFAULT;
+
+	bits = cp->in_bitpos & 7;
+	inpos = (cp->in_bitpos >> 3) - (bits ? 1 : 0);
+	ret = erofs_io_pread(ios->vin, src, sizeof(src), inpos);
+	if (ret < 0)
+		return ret;
+
+	if (bits) {
+		inflatePrime(&strm, bits, src[0] >> (8 - bits));
+		strm.next_in = src + 1;
+		strm.avail_in = ret - 1;
+	} else {
+		strm.next_in = src;
+		strm.avail_in = ret;
+	}
+	inpos += ret;
+	(void)inflateSetDictionary(&strm, cp->window, sizeof(cp->window));
+
+	offset -= cp->outpos;
+	do {
+		/* define where to put uncompressed data, and how much */
+		if (!offset && skip) {          /* at offset now */
+			strm.avail_out = len;
+			strm.next_out = buf;
+			skip = false;		/* only do this once */
+		} else if (offset > sizeof(discard)) {	/* skip WINSIZE bytes */
+			strm.avail_out = sizeof(discard);
+			strm.next_out = discard;
+			offset -= sizeof(discard);
+		} else if (offset) {			/* last skip */
+			strm.avail_out = (unsigned int)offset;
+			strm.next_out = discard;
+			offset = 0;
+		}
+
+		/* uncompress until avail_out filled, or end of stream */
+		do {
+			if (!strm.avail_in) {
+				ret = erofs_io_pread(ios->vin, src, sizeof(src),
+						     inpos);
+				if (ret < 0)
+					return ret;
+				if (!ret)
+					return -EIO;
+				inpos += ret;
+				strm.avail_in = ret;
+				strm.next_in = src;
+			}
+			ret = inflate(&strm, Z_NO_FLUSH);       /* normal inflate */
+			if (ret == Z_NEED_DICT)
+				ret = Z_DATA_ERROR;
+			if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
+				return -EIO;
+			if (ret == Z_STREAM_END)
+				break;
+		} while (strm.avail_out);
+
+		/* if reach end of stream, then don't keep trying to get more */
+		if (ret == Z_STREAM_END)
+			break;
+
+		/* do until offset reached and requested data read, or stream ends */
+	} while (skip);
+	return len - strm.avail_out;
+}
+
+static struct erofs_vfops erofs_gzran_ios_vfops = {
+	.pread = erofs_gzran_ios_vfpread,
+	.close = erofs_gzran_ios_vfclose,
+};
+
+struct erofs_vfile *erofs_gzran_zinfo_open(struct erofs_vfile *vin,
+					   void *zinfo_buf, unsigned int len)
+{
+	struct aws_soci_zinfo_header *h;
+	struct aws_soci_zinfo_ckpt *c;
+	struct erofs_vfile *vf;
+	struct erofs_gzran_iostream *ios;
+	unsigned int v2_size, version;
+	int ret, i;
+
+	if (len < sizeof(*h))
+		return ERR_PTR(-EINVAL);
+
+	vf = malloc(sizeof(*vf) + sizeof(*ios));
+	if (!vf)
+		return ERR_PTR(-ENOMEM);
+
+	ios = (struct erofs_gzran_iostream *)vf->payload;
+	h = zinfo_buf;
+	ios->entries = le32_to_cpu(h->have);
+	ios->span_size = le32_to_cpu(h->span_size);
+
+	v2_size = sizeof(*c) * ios->entries + sizeof(*h);
+	if (v2_size - sizeof(*c) == len) {
+		version = 1;
+	} else if (v2_size == len) {
+		version = 2;
+	} else {
+		ret = -EOPNOTSUPP;
+		goto err_ios;
+	}
+
+	ios->cp = malloc(sizeof(*ios->cp) * ios->entries);
+	if (!ios->cp) {
+		ret = -ENOMEM;
+		goto err_ios;
+	}
+
+	i = 0;
+	if (version == 1) {
+		ios->cp[0] = (struct erofs_gzran_cutpoint) {
+			.in_bitpos = 10 << 3,
+			.outpos = 0,
+		};
+		i = 1;
+	}
+
+	c = (struct aws_soci_zinfo_ckpt *)(h + 1);
+	for (; i < ios->entries; ++i, ++c) {
+		ios->cp[i].in_bitpos = (c->in << 3) | c->bits;
+		ios->cp[i].outpos = c->out;
+		memcpy(ios->cp[i].window, c->window, sizeof(*c->window));
+	}
+	ios->vin = vin;
+	vf->ops = &erofs_gzran_ios_vfops;
+	return vf;
+err_ios:
+	free(vf);
+	return ERR_PTR(ret);
+}
 #else
 struct erofs_gzran_builder *erofs_gzran_builder_init(struct erofs_vfile *vf,
 						     u32 span_size)
@@ -213,4 +383,10 @@ int erofs_gzran_builder_final(struct erofs_gzran_builder *gb)
 {
 	return 0;
 }
+
+struct erofs_vfile *erofs_gzran_zinfo_open(struct erofs_vfile *vin,
+					   void *zinfo_buf, unsigned int len)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
 #endif
diff --git a/lib/liberofs_gzran.h b/lib/liberofs_gzran.h
index 4764506..443fe15 100644
--- a/lib/liberofs_gzran.h
+++ b/lib/liberofs_gzran.h
@@ -18,4 +18,6 @@ int erofs_gzran_builder_export_zinfo(struct erofs_gzran_builder *gb,
 				     struct erofs_vfile *zinfo_vf);
 int erofs_gzran_builder_final(struct erofs_gzran_builder *gb);
 
+struct erofs_vfile *erofs_gzran_zinfo_open(struct erofs_vfile *vin,
+					   void *zinfo_buf, unsigned int len);
 #endif
-- 
2.43.5



More information about the Linux-erofs mailing list