[PATCH v1 2/2] oci: replace layer index with layer digest

ChengyuZhu6 hudson at cyzhu.com
Sat Sep 13 18:27:48 AEST 2025


From: Chengyu Zhu <hudsonzhu at tencent.com>

Replace numeric layer_index with layer_digest string for more precise
and reliable OCI layer identification. This change affects both mkfs.erofs
and mount.erofs tools.

Signed-off-by: Chengyu Zhu <hudsonzhu at tencent.com>
---
 lib/liberofs_oci.h |  6 ++--
 lib/remotes/oci.c  | 72 ++++++++++++++++++++++++++++++++--------------
 mkfs/main.c        | 21 +++++++-------
 mount/main.c       | 33 +++++++++++----------
 4 files changed, 82 insertions(+), 50 deletions(-)

diff --git a/lib/liberofs_oci.h b/lib/liberofs_oci.h
index aa41141..ef8d0ea 100644
--- a/lib/liberofs_oci.h
+++ b/lib/liberofs_oci.h
@@ -21,7 +21,7 @@ struct erofs_importer;
  * @platform: target platform in "os/arch" format (e.g., "linux/amd64")
  * @username: username for authentication (optional)
  * @password: password for authentication (optional)
- * @layer_index: specific layer to extract (-1 for all layers)
+ * @layer_digest: specific layer digest to extract (NULL for all layers)
  *
  * Configuration structure for OCI image parameters including registry
  * location, image identification, platform specification, and authentication
@@ -32,7 +32,7 @@ struct ocierofs_config {
 	char *platform;
 	char *username;
 	char *password;
-	int layer_index;
+	char *layer_digest;
 };
 
 struct ocierofs_layer_info {
@@ -51,7 +51,7 @@ struct ocierofs_ctx {
 	char *tag;
 	char *manifest_digest;
 	struct ocierofs_layer_info **layers;
-	int layer_index;
+	char *layer_digest;
 	int layer_count;
 };
 
diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c
index 26aec27..f6181cc 100644
--- a/lib/remotes/oci.c
+++ b/lib/remotes/oci.c
@@ -898,6 +898,21 @@ static int ocierofs_prepare_auth(struct ocierofs_ctx *ctx,
 	return 0;
 }
 
+static int ocierofs_find_layer_by_digest(struct ocierofs_ctx *ctx, const char *digest)
+{
+	int i;
+
+	if (!digest || !ctx->layers)
+		return -1;
+
+	for (i = 0; i < ctx->layer_count; i++) {
+		if (ctx->layers[i] && ctx->layers[i]->digest &&
+		    !strcmp(ctx->layers[i]->digest, digest))
+			return i;
+	}
+	return -1;
+}
+
 static int ocierofs_prepare_layers(struct ocierofs_ctx *ctx,
 				   const struct ocierofs_config *config)
 {
@@ -925,16 +940,18 @@ static int ocierofs_prepare_layers(struct ocierofs_ctx *ctx,
 		goto out_manifest;
 	}
 
-	if (ctx->layer_index >= ctx->layer_count) {
-		erofs_err("layer index %d exceeds available layers (%d)",
-			  ctx->layer_index, ctx->layer_count);
-		ret = -EINVAL;
-		goto out_layers;
+	if (ctx->layer_digest) {
+		if (ocierofs_find_layer_by_digest(ctx, ctx->layer_digest) < 0) {
+			erofs_err("layer digest %s not found in image layers",
+				  ctx->layer_digest);
+			ret = -ENOENT;
+			goto out_layers;
+		}
 	}
 	return 0;
 
 out_layers:
-	free(ctx->layers);
+	ocierofs_free_layers_info(ctx->layers, ctx->layer_count);
 	ctx->layers = NULL;
 out_manifest:
 	free(ctx->manifest_digest);
@@ -1054,10 +1071,10 @@ static int ocierofs_init(struct ocierofs_ctx *ctx, const struct ocierofs_config
 	if (ocierofs_curl_setup_common_options(ctx->curl))
 		return -EIO;
 
-	if (config->layer_index >= 0)
-		ctx->layer_index = config->layer_index;
+	if (config->layer_digest)
+		ctx->layer_digest = strdup(config->layer_digest);
 	else
-		ctx->layer_index = -1;
+		ctx->layer_digest = NULL;
 	ctx->registry = strdup("registry-1.docker.io");
 	ctx->tag = strdup("latest");
 	if (config->platform)
@@ -1190,6 +1207,7 @@ static void ocierofs_ctx_cleanup(struct ocierofs_ctx *ctx)
 	free(ctx->tag);
 	free(ctx->platform);
 	free(ctx->manifest_digest);
+	free(ctx->layer_digest);
 }
 
 int ocierofs_build_trees(struct erofs_importer *importer,
@@ -1204,8 +1222,13 @@ int ocierofs_build_trees(struct erofs_importer *importer,
 		return ret;
 	}
 
-	if (ctx.layer_index >= 0) {
-		i = ctx.layer_index;
+	if (ctx.layer_digest) {
+		i = ocierofs_find_layer_by_digest(&ctx, ctx.layer_digest);
+		if (i < 0) {
+			erofs_err("layer digest %s not found", ctx.layer_digest);
+			ret = -ENOENT;
+			goto out;
+		}
 		end = i + 1;
 	} else {
 		i = 0;
@@ -1215,25 +1238,26 @@ int ocierofs_build_trees(struct erofs_importer *importer,
 	while (i < end) {
 		char *trimmed = erofs_trim_for_progressinfo(ctx.layers[i]->digest,
 				sizeof("Extracting layer  ...") - 1);
-		erofs_update_progressinfo("Extracting layer %d: %s ...", i,
-				  trimmed);
+		erofs_update_progressinfo("Extracting layer %s ...", trimmed);
 		free(trimmed);
 		fd = ocierofs_extract_layer(&ctx, ctx.layers[i]->digest,
 					    ctx.auth_header);
 		if (fd < 0) {
-			erofs_err("failed to extract layer %d: %s", i,
-				  erofs_strerror(fd));
+			erofs_err("failed to extract layer %s: %s",
+				  ctx.layers[i]->digest, erofs_strerror(fd));
+			ret = fd;
 			break;
 		}
 		ret = ocierofs_process_tar_stream(importer, fd);
 		close(fd);
 		if (ret) {
-			erofs_err("failed to process tar stream for layer %d: %s", i,
-				  erofs_strerror(ret));
+			erofs_err("failed to process tar stream for layer %s: %s",
+				  ctx.layers[i]->digest, erofs_strerror(ret));
 			break;
 		}
 		i++;
 	}
+out:
 	ocierofs_ctx_cleanup(&ctx);
 	return ret;
 }
@@ -1246,12 +1270,18 @@ static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, off_t offset,
 	const char *api_registry;
 	char rangehdr[64];
 	long http_code = 0;
-	int ret;
-	int index = ctx->layer_index;
-	u64 blob_size = ctx->layers[index]->size;
+	int ret, index;
+	const char *digest;
+	u64 blob_size;
 	size_t available;
 	size_t copy_size;
 
+	index = ocierofs_find_layer_by_digest(ctx, ctx->layer_digest);
+	if (index < 0)
+		return -ENOENT;
+	digest = ctx->layer_digest;
+	blob_size = ctx->layers[index]->size;
+
 	if (offset < 0)
 		return -EINVAL;
 
@@ -1265,7 +1295,7 @@ static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, off_t offset,
 
 	api_registry = ocierofs_get_api_registry(ctx->registry);
 	if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s",
-	     api_registry, ctx->repository, ctx->layers[index]->digest) == -1)
+	     api_registry, ctx->repository, digest) == -1)
 		return -ENOMEM;
 
 	if (length)
diff --git a/mkfs/main.c b/mkfs/main.c
index a8208d4..a34e58b 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -213,7 +213,7 @@ static void usage(int argc, char **argv)
 #endif
 #ifdef OCIEROFS_ENABLED
 		" --oci[=platform=X]    X=platform (default: linux/amd64)\n"
-		"   [,layer=Y]          Y=layer index to extract (0-based; omit to extract all layers)\n"
+		"   [,layer=Y]          Y=layer digest to extract (omit to extract all layers)\n"
 		"   [,username=Z]       Z=username for authentication (optional)\n"
 		"   [,password=W]       W=password for authentication (optional)\n"
 #endif
@@ -728,16 +728,15 @@ static int mkfs_parse_oci_options(struct ocierofs_config *oci_cfg, char *options
 			p = strstr(opt, "layer=");
 			if (p) {
 				p += strlen("layer=");
-				{
-					char *endptr;
-					unsigned long v = strtoul(p, &endptr, 10);
+				free(oci_cfg->layer_digest);
 
-					if (endptr == p || *endptr != '\0') {
-						erofs_err("invalid layer index %s",
-						  p);
-						return -EINVAL;
-					}
-					oci_cfg->layer_index = (int)v;
+				if (strncmp(p, "sha256:", 7) != 0) {
+					if (asprintf(&oci_cfg->layer_digest, "sha256:%s", p) < 0)
+						return -ENOMEM;
+				} else {
+					oci_cfg->layer_digest = strdup(p);
+					if (!oci_cfg->layer_digest)
+						return -ENOMEM;
 				}
 			} else {
 				p = strstr(opt, "username=");
@@ -1838,7 +1837,7 @@ int main(int argc, char **argv)
 #endif
 #ifdef OCIEROFS_ENABLED
 		} else if (source_mode == EROFS_MKFS_SOURCE_OCI) {
-			ocicfg.layer_index = -1;
+			ocicfg.layer_digest = NULL;
 
 			err = mkfs_parse_oci_options(&ocicfg, mkfs_oci_options);
 			if (err)
diff --git a/mount/main.c b/mount/main.c
index f368746..7094344 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -85,13 +85,15 @@ static int erofsmount_parse_oci_option(const char *option)
 	p = strstr(option, "oci.layer=");
 	if (p != NULL) {
 		p += strlen("oci.layer=");
-		{
-			char *endptr;
-			unsigned long v = strtoul(p, &endptr, 10);
+		free(oci_cfg->layer_digest);
 
-			if (endptr == p || *endptr != '\0')
-				return -EINVAL;
-			oci_cfg->layer_index = (int)v;
+		if (strncmp(p, "sha256:", 7) != 0) {
+			if (asprintf(&oci_cfg->layer_digest, "sha256:%s", p) < 0)
+				return -ENOMEM;
+		} else {
+			oci_cfg->layer_digest = strdup(p);
+			if (!oci_cfg->layer_digest)
+				return -ENOMEM;
 		}
 	} else {
 		p = strstr(option, "oci.platform=");
@@ -125,7 +127,7 @@ static int erofsmount_parse_oci_option(const char *option)
 	}
 
 	if (oci_cfg->platform || oci_cfg->username || oci_cfg->password ||
-	    oci_cfg->layer_index)
+	    oci_cfg->layer_digest)
 		nbdsrc.type = EROFSNBD_SOURCE_OCI;
 	return 0;
 }
@@ -413,10 +415,10 @@ static int erofsmount_write_recovery_oci(FILE *f, struct erofs_nbd_source *sourc
 		if (IS_ERR(b64cred))
 			return PTR_ERR(b64cred);
 	}
-	ret = fprintf(f, "OCI_LAYER %s %s %d %s\n",
+	ret = fprintf(f, "OCI_LAYER %s %s %s %s\n",
 		       source->ocicfg.image_ref ?: "",
 		       source->ocicfg.platform ?: "",
-		       source->ocicfg.layer_index,
+		       source->ocicfg.layer_digest ?: "",
 		       b64cred ?: "");
 	free(b64cred);
 	return ret < 0 ? -ENOMEM : 0;
@@ -485,8 +487,6 @@ static int erofsmount_parse_recovery_ocilayer(struct ocierofs_config *oci_cfg,
 	int token_count = 0;
 	char *p = source;
 	int err;
-	char *endptr;
-	unsigned long v;
 
 	while (token_count < 4 && (p = strchr(p, ' ')) != NULL) {
 		*p++ = '\0';
@@ -503,10 +503,13 @@ static int erofsmount_parse_recovery_ocilayer(struct ocierofs_config *oci_cfg,
 	oci_cfg->image_ref = source;
 	oci_cfg->platform = tokens[0];
 
-	v = strtoul(tokens[1], &endptr, 10);
-	if (endptr == tokens[1] || *endptr != '\0')
-		return -EINVAL;
-	oci_cfg->layer_index = (int)v;
+	if (tokens[1] && strlen(tokens[1]) > 0) {
+		oci_cfg->layer_digest = strdup(tokens[1]);
+		if (!oci_cfg->layer_digest)
+			return -ENOMEM;
+	} else {
+		oci_cfg->layer_digest = NULL;
+	}
 
 	if (token_count > 2) {
 		err = ocierofs_decode_userpass(tokens[2], &oci_cfg->username,
-- 
2.51.0



More information about the Linux-erofs mailing list