[PATCH v2 2/2] erofs-utils: lib: oci: allow HTTP connections to registry

Yifan Zhao zhaoyifan28 at huawei.com
Fri Dec 5 21:33:22 AEDT 2025


Currently, the URL used to send requests to the registry is hardcoded
with "https://". This patch introduces an optional insecure option for
oci in {mkfs,mount}.erofs, enabling registry access via the HTTP
protocol.

Also, this patch refactors the deeply nested logic in the `--oci`
argument parsing in both {mkfs,mount}/main.c.

Signed-off-by: Yifan Zhao <zhaoyifan28 at huawei.com>
---
diff against v1:
- also add oci.insecure option in mount.erofs

 lib/liberofs_oci.h |   3 ++
 lib/remotes/oci.c  |  40 ++++++++++-------
 mkfs/main.c        |  97 ++++++++++++++++++----------------------
 mount/main.c       | 107 +++++++++++++++++++--------------------------
 4 files changed, 115 insertions(+), 132 deletions(-)

diff --git a/lib/liberofs_oci.h b/lib/liberofs_oci.h
index 5298f18..9e0571f 100644
--- a/lib/liberofs_oci.h
+++ b/lib/liberofs_oci.h
@@ -23,6 +23,7 @@ struct erofs_importer;
  * @password: password for authentication (optional)
  * @blob_digest: specific blob digest to extract (NULL for all layers)
  * @layer_index: specific layer index to extract (negative for all layers)
+ * @insecure: use HTTP for registry communication (optional)
  *
  * Configuration structure for OCI image parameters including registry
  * location, image identification, platform specification, and authentication
@@ -37,6 +38,7 @@ struct ocierofs_config {
 	int layer_index;
 	char *tarindex_path;
 	char *zinfo_path;
+	bool insecure;
 };
 
 struct ocierofs_layer_info {
@@ -57,6 +59,7 @@ struct ocierofs_ctx {
 	struct ocierofs_layer_info **layers;
 	char *blob_digest;
 	int layer_count;
+	const char *schema;
 };
 
 struct ocierofs_iostream {
diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c
index c1d6cae..d5afd6a 100644
--- a/lib/remotes/oci.c
+++ b/lib/remotes/oci.c
@@ -496,8 +496,8 @@ static char *ocierofs_discover_auth_endpoint(struct ocierofs_ctx *ctx,
 
 	api_registry = ocierofs_get_api_registry(registry);
 
-	if (asprintf(&test_url, "https://%s/v2/%s/manifests/nonexistent",
-	     api_registry, repository) < 0)
+	if (asprintf(&test_url, "%s%s/v2/%s/manifests/nonexistent",
+	     ctx->schema, api_registry, repository) < 0)
 		return NULL;
 
 	curl_easy_reset(ctx->curl);
@@ -528,9 +528,9 @@ static char *ocierofs_get_auth_token(struct ocierofs_ctx *ctx, const char *regis
 				     const char *password)
 {
 	static const char * const auth_patterns[] = {
-		"https://%s/v2/auth",
-		"https://auth.%s/token",
-		"https://%s/token",
+		"%s%s/v2/auth",
+		"%sauth.%s/token",
+		"%s%s/token",
 		NULL,
 	};
 	char *auth_header = NULL;
@@ -561,8 +561,8 @@ static char *ocierofs_get_auth_token(struct ocierofs_ctx *ctx, const char *regis
 
 		api_registry = ocierofs_get_api_registry(registry);
 
-		if (asprintf(&test_url, "https://%s/v2/%s/manifests/nonexistent",
-		     api_registry, repository) >= 0) {
+		if (asprintf(&test_url, "%s%s/v2/%s/manifests/nonexistent",
+		     ctx->schema, api_registry, repository) >= 0) {
 			curl_easy_reset(ctx->curl);
 			ocierofs_curl_setup_common_options(ctx->curl);
 
@@ -598,7 +598,7 @@ static char *ocierofs_get_auth_token(struct ocierofs_ctx *ctx, const char *regis
 	for (i = 0; auth_patterns[i]; i++) {
 		char *auth_url;
 
-		if (asprintf(&auth_url, auth_patterns[i], registry) < 0)
+		if (asprintf(&auth_url, auth_patterns[i], ctx->schema, registry) < 0)
 			continue;
 
 		auth_header = ocierofs_get_auth_token_with_url(ctx, auth_url,
@@ -629,8 +629,8 @@ static char *ocierofs_get_manifest_digest(struct ocierofs_ctx *ctx,
 	int ret = 0, len, i;
 
 	api_registry = ocierofs_get_api_registry(registry);
-	if (asprintf(&req.url, "https://%s/v2/%s/manifests/%s",
-	     api_registry, repository, tag) < 0)
+	if (asprintf(&req.url, "%s%s/v2/%s/manifests/%s",
+	     ctx->schema, api_registry, repository, tag) < 0)
 		return ERR_PTR(-ENOMEM);
 
 	if (auth_header && strstr(auth_header, "Bearer"))
@@ -749,8 +749,8 @@ static int ocierofs_fetch_layers_info(struct ocierofs_ctx *ctx)
 	ctx->layer_count = 0;
 	api_registry = ocierofs_get_api_registry(registry);
 
-	if (asprintf(&req.url, "https://%s/v2/%s/manifests/%s",
-		     api_registry, repository, digest) < 0)
+	if (asprintf(&req.url, "%s%s/v2/%s/manifests/%s",
+		     ctx->schema, api_registry, repository, digest) < 0)
 		return -ENOMEM;
 
 	if (auth_header && strstr(auth_header, "Bearer"))
@@ -1124,10 +1124,18 @@ static int ocierofs_init(struct ocierofs_ctx *ctx, const struct ocierofs_config
 	if (!ctx->registry || !ctx->tag || !ctx->platform)
 		return -ENOMEM;
 
+	ctx->schema = config->insecure ? "http://" : "https://";
+
 	ret = ocierofs_parse_ref(ctx, config->image_ref);
 	if (ret)
 		return ret;
 
+	if (config->insecure && (!strcmp(ctx->registry, DOCKER_API_REGISTRY) ||
+				 !strcmp(ctx->registry, DOCKER_REGISTRY))) {
+		erofs_err("Insecure connection to Docker registry is not allowed");
+		return -EINVAL;
+	}
+
 	ret = ocierofs_prepare_layers(ctx, config);
 	if (ret)
 		return ret;
@@ -1152,8 +1160,8 @@ static int ocierofs_download_blob_to_fd(struct ocierofs_ctx *ctx,
 	};
 
 	api_registry = ocierofs_get_api_registry(ctx->registry);
-	if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s",
-	     api_registry, ctx->repository, digest) == -1)
+	if (asprintf(&req.url, "%s%s/v2/%s/blobs/%s",
+	     ctx->schema, api_registry, ctx->repository, digest) == -1)
 		return -ENOMEM;
 
 	if (auth_header && strstr(auth_header, "Bearer"))
@@ -1344,8 +1352,8 @@ static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, off_t offset,
 		length = (size_t)(blob_size - offset);
 
 	api_registry = ocierofs_get_api_registry(ctx->registry);
-	if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s",
-	     api_registry, ctx->repository, digest) == -1)
+	if (asprintf(&req.url, "%s%s/v2/%s/blobs/%s",
+	     ctx->schema, api_registry, ctx->repository, digest) == -1)
 		return -ENOMEM;
 
 	if (length)
diff --git a/mkfs/main.c b/mkfs/main.c
index 7aa8eae..5710948 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -218,11 +218,12 @@ static void usage(int argc, char **argv)
 #endif
 #ifdef OCIEROFS_ENABLED
 		" --oci=[f|i]           generate a full (f) or index-only (i) image from OCI remote source\n"
-		"   [,=platform=X]      X=platform (default: linux/amd64)\n"
+		"   [,platform=X]       X=platform (default: linux/amd64)\n"
 		"   [,layer=#]          #=layer index to extract (0-based; omit to extract all layers)\n"
 		"   [,blob=Y]           Y=blob 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"
+		"   [,insecure]         use HTTP instead of HTTPS (optional)\n"
 #endif
 		" --tar=X               generate a full or index-only image from a tarball(-ish) source\n"
 		"                       (X = f|i|headerball; f=full mode, i=index mode,\n"
@@ -744,7 +745,7 @@ static int mkfs_parse_s3_cfg(char *cfg_str)
  * Parse OCI options string containing comma-separated key=value pairs.
  *
  * Supported options include f|i, platform, blob|layer, username, password,
- * and zinfo.
+ * and insecure.
  *
  * Return: 0 on success, negative errno on failure
  */
@@ -772,67 +773,55 @@ static int mkfs_parse_oci_options(struct ocierofs_config *oci_cfg, char *options
 		if (q)
 			*q = '\0';
 
-
-		p = strstr(opt, "platform=");
-		if (p) {
+		if ((p = strstr(opt, "platform="))) {
 			p += strlen("platform=");
 			free(oci_cfg->platform);
 			oci_cfg->platform = strdup(p);
 			if (!oci_cfg->platform)
 				return -ENOMEM;
-		} else {
-			p = strstr(opt, "blob=");
-			if (p) {
-				p += strlen("blob=");
-				free(oci_cfg->blob_digest);
+		} else if ((p = strstr(opt, "blob="))) {
+			p += strlen("blob=");
+			free(oci_cfg->blob_digest);
 
-				if (oci_cfg->layer_index >= 0) {
-					erofs_err("invalid --oci: blob and layer cannot be set together");
-					return -EINVAL;
-				}
+			if (oci_cfg->layer_index >= 0) {
+				erofs_err("invalid --oci: blob and layer cannot be set together");
+				return -EINVAL;
+			}
 
-				if (!strncmp(p, "sha256:", 7)) {
-					oci_cfg->blob_digest = strdup(p);
-					if (!oci_cfg->blob_digest)
-						return -ENOMEM;
-				} else if (asprintf(&oci_cfg->blob_digest, "sha256:%s", p) < 0) {
+			if (!strncmp(p, "sha256:", 7)) {
+				oci_cfg->blob_digest = strdup(p);
+				if (!oci_cfg->blob_digest)
 					return -ENOMEM;
-				}
-			} else {
-				p = strstr(opt, "layer=");
-				if (p) {
-					p += strlen("layer=");
-					if (oci_cfg->blob_digest) {
-						erofs_err("invalid --oci: layer and blob cannot be set together");
-						return -EINVAL;
-					}
-					idx = strtol(p, NULL, 10);
-					if (idx < 0)
-						return -EINVAL;
-					oci_cfg->layer_index = (int)idx;
-				} else {
-					p = strstr(opt, "username=");
-					if (p) {
-						p += strlen("username=");
-						free(oci_cfg->username);
-						oci_cfg->username = strdup(p);
-						if (!oci_cfg->username)
-							return -ENOMEM;
-					} else {
-						p = strstr(opt, "password=");
-						if (p) {
-							p += strlen("password=");
-							free(oci_cfg->password);
-							oci_cfg->password = strdup(p);
-							if (!oci_cfg->password)
-								return -ENOMEM;
-						} else {
-							erofs_err("mkfs: invalid --oci value %s", opt);
-							return -EINVAL;
-						}
-					}
-				}
+			} else if (asprintf(&oci_cfg->blob_digest, "sha256:%s", p) < 0) {
+				return -ENOMEM;
 			}
+		} else if ((p = strstr(opt, "layer="))) {
+			p += strlen("layer=");
+			if (oci_cfg->blob_digest) {
+				erofs_err("invalid --oci: layer and blob cannot be set together");
+				return -EINVAL;
+			}
+			idx = strtol(p, NULL, 10);
+			if (idx < 0)
+				return -EINVAL;
+			oci_cfg->layer_index = (int)idx;
+		} else if ((p = strstr(opt, "username="))) {
+			p += strlen("username=");
+			free(oci_cfg->username);
+			oci_cfg->username = strdup(p);
+			if (!oci_cfg->username)
+				return -ENOMEM;
+		} else if ((p = strstr(opt, "password="))) {
+			p += strlen("password=");
+			free(oci_cfg->password);
+			oci_cfg->password = strdup(p);
+			if (!oci_cfg->password)
+				return -ENOMEM;
+		} else if ((p = strstr(opt, "insecure"))) {
+			oci_cfg->insecure = true;
+		} else {
+			erofs_err("mkfs: invalid --oci value %s", opt);
+			return -EINVAL;
 		}
 
 		opt = q ? q + 1 : NULL;
diff --git a/mount/main.c b/mount/main.c
index e25134c..b28b8ba 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -88,8 +88,7 @@ static int erofsmount_parse_oci_option(const char *option)
 	char *p;
 	long idx;
 
-	p = strstr(option, "oci.blob=");
-	if (p != NULL) {
+	if ((p = strstr(option, "oci.blob=")) != NULL) {
 		p += strlen("oci.blob=");
 		free(oci_cfg->blob_digest);
 
@@ -103,68 +102,52 @@ static int erofsmount_parse_oci_option(const char *option)
 			if (!oci_cfg->blob_digest)
 				return -ENOMEM;
 		} else if (asprintf(&oci_cfg->blob_digest, "sha256:%s", p) < 0) {
-				return -ENOMEM;
+			return -ENOMEM;
 		}
-	} else {
-		p = strstr(option, "oci.layer=");
-		if (p != NULL) {
-			p += strlen("oci.layer=");
-			if (oci_cfg->blob_digest) {
-				erofs_err("invalid options: oci.layer and oci.blob cannot be set together");
-				return -EINVAL;
-			}
-			idx = strtol(p, NULL, 10);
-			if (idx < 0)
-				return -EINVAL;
-			oci_cfg->layer_index = (int)idx;
-		} else {
-			p = strstr(option, "oci.platform=");
-			if (p != NULL) {
-				p += strlen("oci.platform=");
-				free(oci_cfg->platform);
-				oci_cfg->platform = strdup(p);
-				if (!oci_cfg->platform)
-					return -ENOMEM;
-			} else {
-				p = strstr(option, "oci.username=");
-				if (p != NULL) {
-					p += strlen("oci.username=");
-					free(oci_cfg->username);
-					oci_cfg->username = strdup(p);
-					if (!oci_cfg->username)
-						return -ENOMEM;
-				} else {
-					p = strstr(option, "oci.password=");
-					if (p != NULL) {
-						p += strlen("oci.password=");
-						free(oci_cfg->password);
-						oci_cfg->password = strdup(p);
-						if (!oci_cfg->password)
-							return -ENOMEM;
-					} else {
-						p = strstr(option, "oci.tarindex=");
-						if (p != NULL) {
-							p += strlen("oci.tarindex=");
-							free(oci_cfg->tarindex_path);
-							oci_cfg->tarindex_path = strdup(p);
-							if (!oci_cfg->tarindex_path)
-								return -ENOMEM;
-						} else {
-							p = strstr(option, "oci.zinfo=");
-							if (p != NULL) {
-								p += strlen("oci.zinfo=");
-								free(oci_cfg->zinfo_path);
-								oci_cfg->zinfo_path = strdup(p);
-								if (!oci_cfg->zinfo_path)
-									return -ENOMEM;
-							} else {
-								return -EINVAL;
-							}
-						}
-					}
-				}
-			}
+	} else if ((p = strstr(option, "oci.layer=")) != NULL) {
+		p += strlen("oci.layer=");
+		if (oci_cfg->blob_digest) {
+			erofs_err("invalid options: oci.layer and oci.blob cannot be set together");
+			return -EINVAL;
 		}
+		idx = strtol(p, NULL, 10);
+		if (idx < 0)
+			return -EINVAL;
+		oci_cfg->layer_index = (int)idx;
+	} else if ((p = strstr(option, "oci.platform=")) != NULL) {
+		p += strlen("oci.platform=");
+		free(oci_cfg->platform);
+		oci_cfg->platform = strdup(p);
+		if (!oci_cfg->platform)
+			return -ENOMEM;
+	} else if ((p = strstr(option, "oci.username=")) != NULL) {
+		p += strlen("oci.username=");
+		free(oci_cfg->username);
+		oci_cfg->username = strdup(p);
+		if (!oci_cfg->username)
+			return -ENOMEM;
+	} else if ((p = strstr(option, "oci.password=")) != NULL) {
+		p += strlen("oci.password=");
+		free(oci_cfg->password);
+		oci_cfg->password = strdup(p);
+		if (!oci_cfg->password)
+			return -ENOMEM;
+	} else if ((p = strstr(option, "oci.tarindex=")) != NULL) {
+		p += strlen("oci.tarindex=");
+		free(oci_cfg->tarindex_path);
+		oci_cfg->tarindex_path = strdup(p);
+		if (!oci_cfg->tarindex_path)
+			return -ENOMEM;
+	} else if ((p = strstr(option, "oci.zinfo=")) != NULL) {
+		p += strlen("oci.zinfo=");
+		free(oci_cfg->zinfo_path);
+		oci_cfg->zinfo_path = strdup(p);
+		if (!oci_cfg->zinfo_path)
+			return -ENOMEM;
+	} else if ((p = strstr(option, "oci.insecure")) != NULL) {
+		oci_cfg->insecure = true;
+	} else {
+		return -EINVAL;
 	}
 	return 0;
 }
-- 
2.43.0



More information about the Linux-erofs mailing list