From: Chengyu Zhu <hudson...@tencent.com> Refactor OCI code to improve code organization and maintainability:
Key changes: - Add `ocierofs_get_api_registry()` and unify API endpoint selection. - Implement Bearer token discovery with Basic fallback; cache auth header. - Parse layer metadata (digest, mediaType, size) and add a proper free helper. - Split blob download from tar processing; process tar via a temp fd. - Rework init/teardown into `ocierofs_init()` and `ocierofs_ctx_cleanup()`. - Update mkfs to use `struct ocierofs_config` and new `--oci` parsing. Signed-off-by: Chengyu Zhu <hudson...@tencent.com> --- lib/liberofs_oci.h | 84 ++--- lib/remotes/oci.c | 885 ++++++++++++++++++++++++++------------------- mkfs/main.c | 192 ++-------- 3 files changed, 566 insertions(+), 595 deletions(-) diff --git a/lib/liberofs_oci.h b/lib/liberofs_oci.h index 3a8108b..d119a2b 100644 --- a/lib/liberofs_oci.h +++ b/lib/liberofs_oci.h @@ -8,85 +8,65 @@ #include <stdbool.h> -#define DOCKER_REGISTRY "docker.io" -#define DOCKER_API_REGISTRY "registry-1.docker.io" - #ifdef __cplusplus extern "C" { #endif -struct erofs_inode; -struct CURL; struct erofs_importer; -/** - * struct erofs_oci_params - OCI configuration parameters - * @registry: registry hostname (e.g., "registry-1.docker.io") - * @repository: image repository (e.g., "library/ubuntu") - * @tag: image tag or digest (e.g., "latest" or sha256:...) +/* + * struct ocierofs_config - OCI configuration structure + * @image_ref: OCI image reference (e.g., "ubuntu:latest", "myregistry.com/app:v1.0") * @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) * - * Configuration structure for OCI image parameters including registry - * location, image identification, platform specification, and authentication - * credentials. */ -struct erofs_oci_params { - char *registry; - char *repository; - char *tag; +struct ocierofs_config { + char *image_ref; char *platform; char *username; char *password; int layer_index; }; -/** - * struct erofs_oci - Combined OCI client structure - * @curl: CURL handle for HTTP requests - * @params: OCI configuration parameters - * - * Main OCI client structure combining CURL HTTP client with - * OCI-specific configuration parameters. - */ -struct erofs_oci { - struct CURL *curl; - struct erofs_oci_params params; +struct ocierofs_layer_info { + char *digest; + char *media_type; + u64 size; }; -/* - * ocierofs_init - Initialize OCI client with default parameters - * @oci: OCI client structure to initialize - * - * Return: 0 on success, negative errno on failure - */ -int ocierofs_init(struct erofs_oci *oci); +struct ocierofs_ctx { + struct { + struct CURL *curl; + char *auth_header; + bool using_basic; + } net; -/* - * ocierofs_cleanup - Clean up OCI client and free allocated resources - * @oci: OCI client structure to clean up - */ -void ocierofs_cleanup(struct erofs_oci *oci); + struct { + char *registry; + char *repository; + char *platform; + int layer_index; + char *tag; + char *manifest_digest; + struct ocierofs_layer_info **layers; + int layer_count; + } img; +}; -/* - * erofs_oci_params_set_string - Set a string field with dynamic allocation - * @field: pointer to the string field to set - * @value: string value to set - * - * Return: 0 on success, negative errno on failure - */ -int erofs_oci_params_set_string(char **field, const char *value); +int ocierofs_init(struct ocierofs_ctx *ctx, const struct ocierofs_config *config); /* - * ocierofs_build_trees - Build file trees from OCI container image layers - * @root: root inode to build the file tree under - * @oci: OCI client structure with configured parameters + * ocierofs_build_trees - Build file trees from an OCI container image + * @importer: erofs importer to populate + * @cfg: oci configuration * * Return: 0 on success, negative errno on failure */ -int ocierofs_build_trees(struct erofs_importer *importer, struct erofs_oci *oci); +int ocierofs_build_trees(struct erofs_importer *importer, + const struct ocierofs_config *cfg); #ifdef __cplusplus } diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c index 0fb8c1f..01f1e24 100644 --- a/lib/remotes/oci.c +++ b/lib/remotes/oci.c @@ -21,6 +21,9 @@ #include "liberofs_oci.h" #include "liberofs_private.h" +#define DOCKER_REGISTRY "docker.io" +#define DOCKER_API_REGISTRY "registry-1.docker.io" + #define DOCKER_MEDIATYPE_MANIFEST_V2 \ "application/vnd.docker.distribution.manifest.v2+json" #define DOCKER_MEDIATYPE_MANIFEST_V1 \ @@ -30,28 +33,67 @@ #define OCI_MEDIATYPE_MANIFEST "application/vnd.oci.image.manifest.v1+json" #define OCI_MEDIATYPE_INDEX "application/vnd.oci.image.index.v1+json" -struct erofs_oci_request { +struct ocierofs_request { char *url; struct curl_slist *headers; }; -struct erofs_oci_response { +struct ocierofs_response { char *data; size_t size; long http_code; }; -struct erofs_oci_stream { - struct erofs_tarfile tarfile; +struct ocierofs_stream { const char *digest; int blobfd; }; +static inline const char *ocierofs_get_api_registry(const char *registry) +{ + if (!registry) + return DOCKER_API_REGISTRY; + return !strcmp(registry, DOCKER_REGISTRY) ? DOCKER_API_REGISTRY : registry; +} + +static inline bool ocierofs_is_manifest_list(const char *media_type) +{ + return media_type && (!strcmp(media_type, DOCKER_MEDIATYPE_MANIFEST_LIST) || + !strcmp(media_type, OCI_MEDIATYPE_INDEX)); +} + +static inline bool ocierofs_is_manifest(const char *media_type) +{ + return media_type && (!strcmp(media_type, DOCKER_MEDIATYPE_MANIFEST_V2) || + !strcmp(media_type, OCI_MEDIATYPE_MANIFEST)); +} + +static inline void ocierofs_request_cleanup(struct ocierofs_request *req) +{ + if (!req) + return; + if (req->headers) + curl_slist_free_all(req->headers); + free(req->url); + req->url = NULL; + req->headers = NULL; +} + +static inline void ocierofs_response_cleanup(struct ocierofs_response *resp) +{ + if (!resp) + return; + free(resp->data); + resp->data = NULL; + resp->size = 0; + resp->http_code = 0; +} + static size_t ocierofs_write_callback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; - struct erofs_oci_response *resp = userp; + struct ocierofs_response *resp = userp; char *ptr; if (!resp->data) @@ -72,16 +114,23 @@ static size_t ocierofs_write_callback(void *contents, size_t size, static size_t ocierofs_layer_write_callback(void *contents, size_t size, size_t nmemb, void *userp) { - struct erofs_oci_stream *stream = userp; + struct ocierofs_stream *stream = userp; size_t realsize = size * nmemb; + const char *buf = contents; + size_t written = 0; if (stream->blobfd < 0) return 0; - if (write(stream->blobfd, contents, realsize) != realsize) { - erofs_err("failed to write layer data for layer %s", - stream->digest); - return 0; + while (written < realsize) { + ssize_t n = write(stream->blobfd, buf + written, realsize - written); + + if (n < 0) { + erofs_err("failed to write layer data for layer %s", + stream->digest); + return 0; + } + written += n; } return realsize; } @@ -93,6 +142,14 @@ static int ocierofs_curl_setup_common_options(struct CURL *curl) curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, "ocierofs/" PACKAGE_VERSION); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); +#if defined(CURLOPT_TCP_KEEPIDLE) + curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 30L); +#endif +#if defined(CURLOPT_TCP_KEEPINTVL) + curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 15L); +#endif return 0; } @@ -111,10 +168,10 @@ static int ocierofs_curl_setup_basic_auth(struct CURL *curl, const char *usernam return 0; } -static int ocierofs_curl_clear_auth(struct CURL *curl) +static int ocierofs_curl_clear_auth(struct ocierofs_ctx *ctx) { - curl_easy_setopt(curl, CURLOPT_USERPWD, NULL); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_NONE); + curl_easy_setopt(ctx->net.curl, CURLOPT_USERPWD, NULL); + curl_easy_setopt(ctx->net.curl, CURLOPT_HTTPAUTH, CURLAUTH_NONE); return 0; } @@ -173,20 +230,20 @@ static int ocierofs_curl_perform(struct CURL *curl, long *http_code_out) return 0; } -static int ocierofs_request_perform(struct erofs_oci *oci, - struct erofs_oci_request *req, - struct erofs_oci_response *resp) +static int ocierofs_request_perform(struct ocierofs_ctx *ctx, + struct ocierofs_request *req, + struct ocierofs_response *resp) { int ret; - ret = ocierofs_curl_setup_rq(oci->curl, req->url, + ret = ocierofs_curl_setup_rq(ctx->net.curl, req->url, OCIEROFS_HTTP_GET, req->headers, - ocierofs_write_callback, resp, + ocierofs_write_callback, resp, NULL, NULL); if (ret) return ret; - ret = ocierofs_curl_perform(oci->curl, &resp->http_code); + ret = ocierofs_curl_perform(ctx->net.curl, &resp->http_code); if (ret) return ret; @@ -201,15 +258,10 @@ static int ocierofs_request_perform(struct erofs_oci *oci, * @realm_out: pointer to store realm value * @service_out: pointer to store service value * @scope_out: pointer to store scope value - * - * Parse Bearer authentication header and extract realm, service, and scope - * parameters for subsequent token requests. - * - * Return: 0 on success, negative errno on failure */ static int ocierofs_parse_auth_header(const char *auth_header, - char **realm_out, char **service_out, - char **scope_out) + char **realm_out, char **service_out, + char **scope_out) { char *realm = NULL, *service = NULL, *scope = NULL; static const char * const param_names[] = {"realm=", "service=", "scope="}; @@ -218,7 +270,6 @@ static int ocierofs_parse_auth_header(const char *auth_header, const char *p; int i, ret = 0; - // https://datatracker.ietf.org/doc/html/rfc6750#section-3 if (strncmp(auth_header, "Bearer ", strlen("Bearer "))) return -EINVAL; @@ -226,7 +277,6 @@ static int ocierofs_parse_auth_header(const char *auth_header, if (!header_copy) return -ENOMEM; - /* Clean up header: replace newlines with spaces and remove double spaces */ for (char *q = header_copy; *q; q++) { if (*q == '\n' || *q == '\r') *q = ' '; @@ -274,22 +324,9 @@ out: return ret; } -/** - * ocierofs_extract_www_auth_info - Extract WWW-Authenticate header information - * @resp_data: HTTP response data containing headers - * @realm_out: pointer to store realm value (optional) - * @service_out: pointer to store service value (optional) - * @scope_out: pointer to store scope value (optional) - * - * Extract realm, service, and scope from WWW-Authenticate header in HTTP response. - * This function handles the common pattern of parsing WWW-Authenticate headers - * that appears in multiple places in the OCI authentication flow. - * - * Return: 0 on success, negative errno on failure - */ static int ocierofs_extract_www_auth_info(const char *resp_data, - char **realm_out, char **service_out, - char **scope_out) + char **realm_out, char **service_out, + char **scope_out) { char *www_auth; char *line_end; @@ -333,29 +370,15 @@ static int ocierofs_extract_www_auth_info(const char *resp_data, return ret; } -/** - * ocierofs_get_auth_token_with_url - Get authentication token from auth server - * @oci: OCI client structure - * @auth_url: authentication server URL - * @service: service name for authentication - * @repository: repository name - * @username: username for basic auth (optional) - * @password: password for basic auth (optional) - * - * Request authentication token from the specified auth server URL using - * basic authentication if credentials are provided. - * - * Return: authentication header string on success, ERR_PTR on failure - */ -static char *ocierofs_get_auth_token_with_url(struct erofs_oci *oci, - const char *auth_url, - const char *service, - const char *repository, - const char *username, - const char *password) +static char *ocierofs_get_auth_token_with_url(struct ocierofs_ctx *ctx, + const char *auth_url, + const char *service, + const char *repository, + const char *username, + const char *password) { - struct erofs_oci_request req = {}; - struct erofs_oci_response resp = {}; + struct ocierofs_request req = {}; + struct ocierofs_response resp = {}; json_object *root, *token_obj, *access_token_obj; const char *token; char *auth_header = NULL; @@ -370,14 +393,14 @@ static char *ocierofs_get_auth_token_with_url(struct erofs_oci *oci, } if (username && password && *username) { - ret = ocierofs_curl_setup_basic_auth(oci->curl, username, - password); + ret = ocierofs_curl_setup_basic_auth(ctx->net.curl, username, + password); if (ret) goto out_url; } - ret = ocierofs_request_perform(oci, &req, &resp); - ocierofs_curl_clear_auth(oci->curl); + ret = ocierofs_request_perform(ctx, &req, &resp); + ocierofs_curl_clear_auth(ctx); if (ret) goto out_url; @@ -391,7 +414,7 @@ static char *ocierofs_get_auth_token_with_url(struct erofs_oci *oci, if (!root) { erofs_err("failed to parse auth response"); ret = -EINVAL; - goto out_url; + goto out_json; } if (!json_object_object_get_ex(root, "token", &token_obj) && @@ -416,16 +439,16 @@ static char *ocierofs_get_auth_token_with_url(struct erofs_oci *oci, out_json: json_object_put(root); out_url: - free(req.url); - free(resp.data); + ocierofs_response_cleanup(&resp); + ocierofs_request_cleanup(&req); return ret ? ERR_PTR(ret) : auth_header; } -static char *ocierofs_discover_auth_endpoint(struct erofs_oci *oci, - const char *registry, - const char *repository) +static char *ocierofs_discover_auth_endpoint(struct ocierofs_ctx *ctx, + const char *registry, + const char *repository) { - struct erofs_oci_response resp = {}; + struct ocierofs_response resp = {}; char *realm = NULL; char *service = NULL; char *result = NULL; @@ -434,20 +457,20 @@ static char *ocierofs_discover_auth_endpoint(struct erofs_oci *oci, CURLcode res; long http_code; - api_registry = (!strcmp(registry, DOCKER_REGISTRY)) ? DOCKER_API_REGISTRY : registry; + api_registry = ocierofs_get_api_registry(registry); if (asprintf(&test_url, "https://%s/v2/%s/manifests/nonexistent", api_registry, repository) < 0) return NULL; - curl_easy_reset(oci->curl); - ocierofs_curl_setup_common_options(oci->curl); + curl_easy_reset(ctx->net.curl); + ocierofs_curl_setup_common_options(ctx->net.curl); - ocierofs_curl_setup_rq(oci->curl, test_url, OCIEROFS_HTTP_HEAD, NULL, + ocierofs_curl_setup_rq(ctx->net.curl, test_url, OCIEROFS_HTTP_HEAD, NULL, NULL, NULL, ocierofs_write_callback, &resp); - res = curl_easy_perform(oci->curl); - curl_easy_getinfo(oci->curl, CURLINFO_RESPONSE_CODE, &http_code); + res = curl_easy_perform(ctx->net.curl); + curl_easy_getinfo(ctx->net.curl, CURLINFO_RESPONSE_CODE, &http_code); if (res == CURLE_OK && (http_code == 401 || http_code == 403 || http_code == 404) && resp.data) { @@ -458,14 +481,14 @@ static char *ocierofs_discover_auth_endpoint(struct erofs_oci *oci, } free(realm); free(service); - free(resp.data); + ocierofs_response_cleanup(&resp); free(test_url); return result; } -static char *ocierofs_get_auth_token(struct erofs_oci *oci, const char *registry, - const char *repository, const char *username, - const char *password) +static char *ocierofs_get_auth_token(struct ocierofs_ctx *ctx, const char *registry, + const char *repository, const char *username, + const char *password) { static const char * const auth_patterns[] = { "https://%s/v2/auth", @@ -484,35 +507,35 @@ static char *ocierofs_get_auth_token(struct erofs_oci *oci, const char *registry !strcmp(registry, DOCKER_REGISTRY); if (docker_reg) { service = "registry.docker.io"; - auth_header = ocierofs_get_auth_token_with_url(oci, + auth_header = ocierofs_get_auth_token_with_url(ctx, "https://auth.docker.io/token", service, repository, username, password); if (!IS_ERR(auth_header)) return auth_header; } - discovered_auth_url = ocierofs_discover_auth_endpoint(oci, registry, repository); + discovered_auth_url = ocierofs_discover_auth_endpoint(ctx, registry, repository); if (discovered_auth_url) { const char *api_registry, *auth_service; - struct erofs_oci_response resp = {}; + struct ocierofs_response resp = {}; char *test_url; CURLcode res; long http_code; - api_registry = (!strcmp(registry, DOCKER_REGISTRY)) ? DOCKER_API_REGISTRY : registry; + api_registry = ocierofs_get_api_registry(registry); if (asprintf(&test_url, "https://%s/v2/%s/manifests/nonexistent", api_registry, repository) >= 0) { - curl_easy_reset(oci->curl); - ocierofs_curl_setup_common_options(oci->curl); + curl_easy_reset(ctx->net.curl); + ocierofs_curl_setup_common_options(ctx->net.curl); - ocierofs_curl_setup_rq(oci->curl, test_url, - OCIEROFS_HTTP_HEAD, NULL, - NULL, NULL, - ocierofs_write_callback, &resp); + ocierofs_curl_setup_rq(ctx->net.curl, test_url, + OCIEROFS_HTTP_HEAD, NULL, + NULL, NULL, + ocierofs_write_callback, &resp); - res = curl_easy_perform(oci->curl); - curl_easy_getinfo(oci->curl, CURLINFO_RESPONSE_CODE, &http_code); + res = curl_easy_perform(ctx->net.curl); + curl_easy_getinfo(ctx->net.curl, CURLINFO_RESPONSE_CODE, &http_code); if (res == CURLE_OK && (http_code == 401 || http_code == 403 || http_code == 404) && resp.data) { @@ -521,14 +544,14 @@ static char *ocierofs_get_auth_token(struct erofs_oci *oci, const char *registry ocierofs_extract_www_auth_info(resp.data, &realm, &discovered_service, NULL); free(realm); } - free(resp.data); + ocierofs_response_cleanup(&resp); free(test_url); } auth_service = discovered_service ? discovered_service : service; - auth_header = ocierofs_get_auth_token_with_url(oci, discovered_auth_url, - auth_service, repository, - username, password); + auth_header = ocierofs_get_auth_token_with_url(ctx, discovered_auth_url, + auth_service, repository, + username, password); free(discovered_auth_url); free(discovered_service); if (!IS_ERR(auth_header)) @@ -541,9 +564,9 @@ static char *ocierofs_get_auth_token(struct erofs_oci *oci, const char *registry if (asprintf(&auth_url, auth_patterns[i], registry) < 0) continue; - auth_header = ocierofs_get_auth_token_with_url(oci, auth_url, - service, repository, - username, password); + auth_header = ocierofs_get_auth_token_with_url(ctx, auth_url, + service, repository, + username, password); free(auth_url); if (!IS_ERR(auth_header)) @@ -554,24 +577,24 @@ static char *ocierofs_get_auth_token(struct erofs_oci *oci, const char *registry return ERR_PTR(-ENOENT); } -static char *ocierofs_get_manifest_digest(struct erofs_oci *oci, - const char *registry, - const char *repository, const char *tag, - const char *platform, - const char *auth_header) +static char *ocierofs_get_manifest_digest(struct ocierofs_ctx *ctx, + const char *registry, + const char *repository, const char *tag, + const char *platform, + const char *auth_header) { - struct erofs_oci_request req = {}; - struct erofs_oci_response resp = {}; + struct ocierofs_request req = {}; + struct ocierofs_response resp = {}; json_object *root, *manifests, *manifest, *platform_obj, *arch_obj; json_object *os_obj, *digest_obj, *schema_obj, *media_type_obj; char *digest = NULL; const char *api_registry; int ret = 0, len, i; - if (!registry || !repository || !tag || !platform) + if (!registry || !repository || !tag) return ERR_PTR(-EINVAL); - api_registry = (!strcmp(registry, DOCKER_REGISTRY)) ? DOCKER_API_REGISTRY : registry; + api_registry = ocierofs_get_api_registry(registry); if (asprintf(&req.url, "https://%s/v2/%s/manifests/%s", api_registry, repository, tag) < 0) return ERR_PTR(-ENOMEM); @@ -581,10 +604,10 @@ static char *ocierofs_get_manifest_digest(struct erofs_oci *oci, req.headers = curl_slist_append(req.headers, "Accept: " DOCKER_MEDIATYPE_MANIFEST_LIST "," - OCI_MEDIATYPE_INDEX "," DOCKER_MEDIATYPE_MANIFEST_V1 "," - DOCKER_MEDIATYPE_MANIFEST_V2); + OCI_MEDIATYPE_INDEX "," OCI_MEDIATYPE_MANIFEST "," + DOCKER_MEDIATYPE_MANIFEST_V1 "," DOCKER_MEDIATYPE_MANIFEST_V2); - ret = ocierofs_request_perform(oci, &req, &resp); + ret = ocierofs_request_perform(ctx, &req, &resp); if (ret) goto out; @@ -612,8 +635,7 @@ static char *ocierofs_get_manifest_digest(struct erofs_oci *oci, if (json_object_object_get_ex(root, "mediaType", &media_type_obj)) { const char *media_type = json_object_get_string(media_type_obj); - if (!strcmp(media_type, DOCKER_MEDIATYPE_MANIFEST_V2) || - !strcmp(media_type, OCI_MEDIATYPE_MANIFEST)) { + if (ocierofs_is_manifest(media_type)) { digest = strdup(tag); ret = 0; goto out_json; @@ -631,9 +653,9 @@ static char *ocierofs_get_manifest_digest(struct erofs_oci *oci, manifest = json_object_array_get_idx(manifests, i); if (json_object_object_get_ex(manifest, "platform", - &platform_obj) && + &platform_obj) && json_object_object_get_ex(platform_obj, "architecture", - &arch_obj) && + &arch_obj) && json_object_object_get_ex(platform_obj, "os", &os_obj) && json_object_object_get_ex(manifest, "digest", &digest_obj)) { const char *arch = json_object_get_string(arch_obj); @@ -655,34 +677,47 @@ static char *ocierofs_get_manifest_digest(struct erofs_oci *oci, out_json: json_object_put(root); out: - free(resp.data); - if (req.headers) - curl_slist_free_all(req.headers); - free(req.url); - + ocierofs_response_cleanup(&resp); + ocierofs_request_cleanup(&req); return ret ? ERR_PTR(ret) : digest; } -static char **ocierofs_get_layers_info(struct erofs_oci *oci, - const char *registry, - const char *repository, - const char *digest, - const char *auth_header, - int *layer_count) +static void ocierofs_free_layers_info(struct ocierofs_layer_info **layers, int count) +{ + int i; + + if (!layers) + return; + + for (i = 0; i < count; i++) { + if (layers[i]) { + free(layers[i]->digest); + free(layers[i]->media_type); + free(layers[i]); + } + } + free(layers); +} + +static struct ocierofs_layer_info **ocierofs_fetch_layers_info(struct ocierofs_ctx *ctx, + const char *registry, + const char *repository, + const char *digest, + const char *auth_header, + int *layer_count) { - struct erofs_oci_request req = {}; - struct erofs_oci_response resp = {}; - json_object *root, *layers, *layer, *digest_obj; - char **layers_info = NULL; + struct ocierofs_request req = {}; + struct ocierofs_response resp = {}; + json_object *root, *layers, *layer, *digest_obj, *media_type_obj, *size_obj; + struct ocierofs_layer_info **layers_info = NULL; const char *api_registry; - int ret, len, i, j; + int ret, len, i; if (!registry || !repository || !digest || !layer_count) return ERR_PTR(-EINVAL); *layer_count = 0; - api_registry = (!strcmp(registry, DOCKER_REGISTRY) ? - DOCKER_API_REGISTRY : registry); + api_registry = ocierofs_get_api_registry(registry); if (asprintf(&req.url, "https://%s/v2/%s/manifests/%s", api_registry, repository, digest) < 0) @@ -694,7 +729,7 @@ static char **ocierofs_get_layers_info(struct erofs_oci *oci, req.headers = curl_slist_append(req.headers, "Accept: " OCI_MEDIATYPE_MANIFEST "," DOCKER_MEDIATYPE_MANIFEST_V2); - ret = ocierofs_request_perform(oci, &req, &resp); + ret = ocierofs_request_perform(ctx, &req, &resp); if (ret) goto out; @@ -725,7 +760,7 @@ static char **ocierofs_get_layers_info(struct erofs_oci *oci, goto out_json; } - layers_info = calloc(len, sizeof(char *)); + layers_info = calloc(len, sizeof(*layers_info)); if (!layers_info) { ret = -ENOMEM; goto out_json; @@ -740,329 +775,421 @@ static char **ocierofs_get_layers_info(struct erofs_oci *oci, goto out_free; } - layers_info[i] = strdup(json_object_get_string(digest_obj)); + layers_info[i] = calloc(1, sizeof(**layers_info)); if (!layers_info[i]) { ret = -ENOMEM; goto out_free; } + layers_info[i]->digest = strdup(json_object_get_string(digest_obj)); + if (!layers_info[i]->digest) { + ret = -ENOMEM; + goto out_free; + } + if (json_object_object_get_ex(layer, "mediaType", &media_type_obj)) + layers_info[i]->media_type = strdup(json_object_get_string(media_type_obj)); + else + layers_info[i]->media_type = NULL; + + if (json_object_object_get_ex(layer, "size", &size_obj)) + layers_info[i]->size = json_object_get_int64(size_obj); + else + layers_info[i]->size = 0; } *layer_count = len; json_object_put(root); - free(resp.data); - if (req.headers) - curl_slist_free_all(req.headers); - free(req.url); + ocierofs_response_cleanup(&resp); + ocierofs_request_cleanup(&req); return layers_info; out_free: - if (layers_info) { - for (j = 0; j < i; j++) - free(layers_info[j]); - } - free(layers_info); + ocierofs_free_layers_info(layers_info, i); out_json: json_object_put(root); out: - free(resp.data); - if (req.headers) - curl_slist_free_all(req.headers); - free(req.url); + ocierofs_response_cleanup(&resp); + ocierofs_request_cleanup(&req); return ERR_PTR(ret); } -static int ocierofs_extract_layer(struct erofs_oci *oci, struct erofs_importer *importer, - const char *digest, const char *auth_header) +static int ocierofs_process_tar_stream(struct erofs_importer *importer, int fd) { - struct erofs_oci_request req = {}; - struct erofs_oci_stream stream = {}; - const char *api_registry; - long http_code; + struct erofs_tarfile tarfile = {}; int ret; - stream = (struct erofs_oci_stream) { - .digest = digest, - .blobfd = erofs_tmpfile(), - }; - if (stream.blobfd < 0) { - erofs_err("failed to create temporary file for %s", digest); - return -errno; - } - - api_registry = (!strcmp(oci->params.registry, DOCKER_REGISTRY)) ? - DOCKER_API_REGISTRY : oci->params.registry; - if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s", - api_registry, oci->params.repository, digest) == -1) { - ret = -ENOMEM; - goto out; - } - - if (auth_header && strstr(auth_header, "Bearer")) - req.headers = curl_slist_append(req.headers, auth_header); - - curl_easy_reset(oci->curl); - - ret = ocierofs_curl_setup_common_options(oci->curl); - if (ret) - goto out; - - ret = ocierofs_curl_setup_rq(oci->curl, req.url, OCIEROFS_HTTP_GET, - req.headers, - ocierofs_layer_write_callback, - &stream, NULL, NULL); - if (ret) - goto out; - - ret = ocierofs_curl_perform(oci->curl, &http_code); - if (ret) - goto out; - - if (http_code < 200 || http_code >= 300) { - erofs_err("HTTP request failed with code %ld", http_code); - ret = -EIO; - goto out; - } + init_list_head(&tarfile.global.xattrs); - if (lseek(stream.blobfd, 0, SEEK_SET) < 0) { - erofs_err("failed to seek to beginning of temp file: %s", - strerror(errno)); - ret = -errno; - goto out; - } - - memset(&stream.tarfile, 0, sizeof(stream.tarfile)); - init_list_head(&stream.tarfile.global.xattrs); - - ret = erofs_iostream_open(&stream.tarfile.ios, stream.blobfd, - EROFS_IOS_DECODER_GZIP); + ret = erofs_iostream_open(&tarfile.ios, fd, EROFS_IOS_DECODER_GZIP); if (ret) { erofs_err("failed to initialize tar stream: %s", erofs_strerror(ret)); - goto out; + return ret; } do { - ret = tarerofs_parse_tar(importer, &stream.tarfile); + ret = tarerofs_parse_tar(importer, &tarfile); /* Continue parsing until end of archive */ } while (!ret); - erofs_iostream_close(&stream.tarfile.ios); + erofs_iostream_close(&tarfile.ios); if (ret < 0 && ret != -ENODATA) { erofs_err("failed to process tar stream: %s", erofs_strerror(ret)); - goto out; + return ret; } - ret = 0; -out: - if (stream.blobfd >= 0) - close(stream.blobfd); - if (req.headers) - curl_slist_free_all(req.headers); - free(req.url); - return ret; + return 0; } -/** - * ocierofs_build_trees - Build file trees from OCI container image layers - * @importer: EROFS importer structure - * @oci: OCI client structure with configured parameters - * - * Extract and build file system trees from all layers of an OCI container - * image. - * - * Return: 0 on success, negative errno on failure - */ -int ocierofs_build_trees(struct erofs_importer *importer, struct erofs_oci *oci) +static int ocierofs_prepare_auth(struct ocierofs_ctx *ctx, + const char *username, + const char *password) { char *auth_header = NULL; - char *manifest_digest = NULL; - char **layers = NULL; - int layer_count = 0; - int ret, i; - - if (!importer || !oci) - return -EINVAL; + int ret = 0; + + ctx->net.using_basic = false; + free(ctx->net.auth_header); + ctx->net.auth_header = NULL; + + /* Try to get a Bearer token first (with credentials if provided) */ + auth_header = ocierofs_get_auth_token(ctx, + ctx->img.registry, + ctx->img.repository, + username, password); + if (!IS_ERR(auth_header)) { + ctx->net.auth_header = auth_header; + return 0; + } - if (oci->params.username && oci->params.password && - oci->params.username[0] && oci->params.password[0]) { - auth_header = ocierofs_get_auth_token(oci, - oci->params.registry, - oci->params.repository, - oci->params.username, - oci->params.password); - if (IS_ERR(auth_header)) { - auth_header = NULL; - ret = ocierofs_curl_setup_basic_auth(oci->curl, - oci->params.username, - oci->params.password); - if (ret) - goto out; - } - } else { - auth_header = ocierofs_get_auth_token(oci, - oci->params.registry, - oci->params.repository, - NULL, NULL); - if (IS_ERR(auth_header)) - auth_header = NULL; + /* If token failed but credentials are provided, fall back to Basic */ + if (username && password && *username && *password) { + ret = ocierofs_curl_setup_basic_auth(ctx->net.curl, + username, password); + if (ret) + return ret; + ctx->net.using_basic = true; } + return 0; +} - manifest_digest = ocierofs_get_manifest_digest(oci, oci->params.registry, - oci->params.repository, - oci->params.tag, - oci->params.platform, - auth_header); - if (IS_ERR(manifest_digest)) { - ret = PTR_ERR(manifest_digest); +static int ocierofs_prepare_layers(struct ocierofs_ctx *ctx, + const struct ocierofs_config *config) +{ + int ret; + + ret = ocierofs_prepare_auth(ctx, config ? config->username : NULL, + config ? config->password : NULL); + if (ret) + return ret; + + ctx->img.manifest_digest = ocierofs_get_manifest_digest(ctx, ctx->img.registry, + ctx->img.repository, + ctx->img.tag, + ctx->img.platform, + ctx->net.auth_header); + if (IS_ERR(ctx->img.manifest_digest)) { + ret = PTR_ERR(ctx->img.manifest_digest); erofs_err("failed to get manifest digest: %s", erofs_strerror(ret)); + ctx->img.manifest_digest = NULL; goto out_auth; } - layers = ocierofs_get_layers_info(oci, oci->params.registry, - oci->params.repository, - manifest_digest, auth_header, - &layer_count); - if (IS_ERR(layers)) { - ret = PTR_ERR(layers); + ctx->img.layers = ocierofs_fetch_layers_info(ctx, ctx->img.registry, + ctx->img.repository, + ctx->img.manifest_digest, ctx->net.auth_header, + &ctx->img.layer_count); + if (IS_ERR(ctx->img.layers)) { + ret = PTR_ERR(ctx->img.layers); erofs_err("failed to get image layers: %s", erofs_strerror(ret)); + ctx->img.layers = NULL; goto out_manifest; } - if (oci->params.layer_index >= 0) { - if (oci->params.layer_index >= layer_count) { + if (ctx->img.layer_index >= 0) { + if (ctx->img.layer_index >= ctx->img.layer_count) { erofs_err("layer index %d exceeds available layers (%d)", - oci->params.layer_index, layer_count); + ctx->img.layer_index, ctx->img.layer_count); ret = -EINVAL; goto out_layers; } - layer_count = 1; - i = oci->params.layer_index; + ctx->img.layer_count = 1; } else { - i = 0; + ctx->img.layer_index = 0; } - while (i < layer_count) { - char *trimmed = erofs_trim_for_progressinfo(layers[i], - sizeof("Extracting layer ...") - 1); - erofs_update_progressinfo("Extracting layer %d: %s ...", i, - trimmed); - free(trimmed); - ret = ocierofs_extract_layer(oci, importer, layers[i], - auth_header); - if (ret) { - erofs_err("failed to extract layer %d: %s", i, - erofs_strerror(ret)); - break; - } - i++; - } + return 0; + out_layers: - for (i = 0; i < layer_count; i++) - free(layers[i]); - free(layers); + free(ctx->img.layers); + ctx->img.layers = NULL; out_manifest: - free(manifest_digest); + free(ctx->img.manifest_digest); + ctx->img.manifest_digest = NULL; out_auth: - free(auth_header); - - if (oci->params.username && oci->params.password && - oci->params.username[0] && oci->params.password[0] && - !auth_header) { - ocierofs_curl_clear_auth(oci->curl); - } -out: + free(ctx->net.auth_header); + ctx->net.auth_header = NULL; + if (ctx->net.using_basic) + ocierofs_curl_clear_auth(ctx); return ret; } -/** - * ocierofs_init - Initialize OCI client with default parameters - * @oci: OCI client structure to initialize - * - * Initialize OCI client structure, set up CURL handle, and configure - * default parameters including platform (linux/amd64), registry - * (registry-1.docker.io), and tag (latest). - * - * Return: 0 on success, negative errno on failure - */ -int ocierofs_init(struct erofs_oci *oci) +static int ocierofs_parse_ref(struct ocierofs_ctx *ctx, const char *ref_str) { - if (!oci) + char *slash, *colon, *dot; + const char *repo_part; + size_t len; + char *tmp; + + if (!ctx || !ref_str) return -EINVAL; - *oci = (struct erofs_oci){}; - oci->curl = curl_easy_init(); - if (!oci->curl) + slash = strchr(ref_str, '/'); + if (slash) { + dot = strchr(ref_str, '.'); + if (dot && dot < slash) { + len = slash - ref_str; + tmp = strndup(ref_str, len); + if (!tmp) + return -ENOMEM; + free(ctx->img.registry); + ctx->img.registry = tmp; + repo_part = slash + 1; + } else { + repo_part = ref_str; + } + } else { + repo_part = ref_str; + } + + colon = strchr(repo_part, ':'); + if (colon) { + len = colon - repo_part; + tmp = strndup(repo_part, len); + if (!tmp) + return -ENOMEM; + + if (!strchr(tmp, '/') && + (!strcmp(ctx->img.registry, DOCKER_API_REGISTRY) || + !strcmp(ctx->img.registry, DOCKER_REGISTRY))) { + char *full_repo; + + if (asprintf(&full_repo, "library/%s", tmp) == -1) { + free(tmp); + return -ENOMEM; + } + free(tmp); + tmp = full_repo; + } + free(ctx->img.repository); + ctx->img.repository = tmp; + + free(ctx->img.tag); + ctx->img.tag = strdup(colon + 1); + if (!ctx->img.tag) + return -ENOMEM; + } else { + tmp = strdup(repo_part); + if (!tmp) + return -ENOMEM; + + if (!strchr(tmp, '/') && + (!strcmp(ctx->img.registry, DOCKER_API_REGISTRY) || + !strcmp(ctx->img.registry, DOCKER_REGISTRY))) { + + char *full_repo; + if (asprintf(&full_repo, "library/%s", tmp) == -1) { + free(tmp); + return -ENOMEM; + } + free(tmp); + tmp = full_repo; + } + free(ctx->img.repository); + ctx->img.repository = tmp; + } + return 0; +} + +int ocierofs_init(struct ocierofs_ctx *ctx, const struct ocierofs_config *config) +{ + int ret; + + ctx->net.curl = curl_easy_init(); + if (!ctx->net.curl) return -EIO; - if (ocierofs_curl_setup_common_options(oci->curl)) { - ocierofs_cleanup(oci); + if (ocierofs_curl_setup_common_options(ctx->net.curl)) return -EIO; - } - if (erofs_oci_params_set_string(&oci->params.platform, - "linux/amd64") || - erofs_oci_params_set_string(&oci->params.registry, - DOCKER_API_REGISTRY) || - erofs_oci_params_set_string(&oci->params.tag, "latest")) { - ocierofs_cleanup(oci); + ctx->img.layer_index = -1; + ctx->img.registry = strdup("registry-1.docker.io"); + ctx->img.tag = strdup("latest"); + if (config && config->platform) + ctx->img.platform = strdup(config->platform); + else + ctx->img.platform = strdup("linux/amd64"); + if (!ctx->img.registry || !ctx->img.tag || !ctx->img.platform) return -ENOMEM; - } - oci->params.layer_index = -1; /* -1 means extract all layers */ + + if (config && config->layer_index >= 0) + ctx->img.layer_index = config->layer_index; + + ret = ocierofs_parse_ref(ctx, config->image_ref); + if (ret) + return ret; + + ret = ocierofs_prepare_layers(ctx, config); + if (ret) + return ret; + return 0; } -/** - * ocierofs_cleanup - Clean up OCI client and free allocated resources - * @oci: OCI client structure to clean up - * - * Clean up CURL handle, free all allocated string parameters, and - * reset the OCI client structure to a clean state. - */ -void ocierofs_cleanup(struct erofs_oci *oci) +static int ocierofs_download_blob_to_fd(struct ocierofs_ctx *ctx, + const char *digest, + const char *auth_header, + int outfd) { - if (!oci) - return; + struct ocierofs_request req = {}; + struct ocierofs_stream stream = {}; + const char *api_registry; + long http_code; + int ret; + + stream = (struct ocierofs_stream) { + .digest = digest, + .blobfd = outfd, + }; - if (oci->curl) { - curl_easy_cleanup(oci->curl); - oci->curl = NULL; + api_registry = ocierofs_get_api_registry(ctx->img.registry); + if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s", + api_registry, ctx->img.repository, digest) == -1) + return -ENOMEM; + + if (auth_header && strstr(auth_header, "Bearer")) + req.headers = curl_slist_append(req.headers, auth_header); + + curl_easy_reset(ctx->net.curl); + + ret = ocierofs_curl_setup_common_options(ctx->net.curl); + if (ret) + goto out; + + ret = ocierofs_curl_setup_rq(ctx->net.curl, req.url, OCIEROFS_HTTP_GET, + req.headers, + ocierofs_layer_write_callback, + &stream, NULL, NULL); + if (ret) + goto out; + + ret = ocierofs_curl_perform(ctx->net.curl, &http_code); + if (ret) + goto out; + + if (http_code < 200 || http_code >= 300) { + erofs_err("HTTP request failed with code %ld", http_code); + ret = -EIO; + goto out; + } + ret = 0; +out: + ocierofs_request_cleanup(&req); + return ret; +} + +static int ocierofs_extract_layer(struct ocierofs_ctx *ctx, + const char *digest, const char *auth_header) +{ + struct ocierofs_stream stream = {}; + int ret; + + stream = (struct ocierofs_stream) { + .digest = digest, + .blobfd = erofs_tmpfile(), + }; + if (stream.blobfd < 0) { + erofs_err("failed to create temporary file for %s", digest); + return -errno; + } + + ret = ocierofs_download_blob_to_fd(ctx, digest, auth_header, stream.blobfd); + if (ret) + goto out; + + if (lseek(stream.blobfd, 0, SEEK_SET) < 0) { + erofs_err("failed to seek to beginning of temp file: %s", + strerror(errno)); + ret = -errno; + goto out; } - free(oci->params.registry); - free(oci->params.repository); - free(oci->params.tag); - free(oci->params.platform); - free(oci->params.username); - free(oci->params.password); - - oci->params.registry = NULL; - oci->params.repository = NULL; - oci->params.tag = NULL; - oci->params.platform = NULL; - oci->params.username = NULL; - oci->params.password = NULL; + return stream.blobfd; + +out: + if (stream.blobfd >= 0) + close(stream.blobfd); + return ret; } -int erofs_oci_params_set_string(char **field, const char *value) +static void ocierofs_ctx_cleanup(struct ocierofs_ctx *ctx) { - char *new_value; + if (!ctx) + return; - if (!field) - return -EINVAL; + if (ctx->net.curl) { + curl_easy_cleanup(ctx->net.curl); + ctx->net.curl = NULL; + } + free(ctx->net.auth_header); + ctx->net.auth_header = NULL; + + ocierofs_free_layers_info(ctx->img.layers, ctx->img.layer_count); + free(ctx->img.registry); + free(ctx->img.repository); + free(ctx->img.tag); + free(ctx->img.platform); + free(ctx->img.manifest_digest); + memset(&ctx->img, 0, sizeof(ctx->img)); +} - if (!value) { - free(*field); - *field = NULL; - return 0; +int ocierofs_build_trees(struct erofs_importer *importer, + const struct ocierofs_config *config) +{ + struct ocierofs_ctx ctx = {}; + int ret, i; + + ret = ocierofs_init(&ctx, config); + if (ret) { + ocierofs_ctx_cleanup(&ctx); + return ret; } - new_value = strdup(value); - if (!new_value) - return -ENOMEM; + i = ctx.img.layer_index; + while (i < ctx.img.layer_count) { + char *trimmed = erofs_trim_for_progressinfo(ctx.img.layers[i]->digest, + sizeof("Extracting layer ...") - 1); + erofs_update_progressinfo("Extracting layer %d: %s ...", i, + trimmed); + free(trimmed); + int fd = ocierofs_extract_layer(&ctx, ctx.img.layers[i]->digest, + ctx.net.auth_header); + if (fd < 0) { + erofs_err("failed to extract layer %d: %s", i, + erofs_strerror(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)); + break; + } + i++; + } - free(*field); - *field = new_value; - return 0; + ocierofs_ctx_cleanup(&ctx); + return ret; } diff --git a/mkfs/main.c b/mkfs/main.c index bc895f1..5e8da7a 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -150,18 +150,18 @@ static void usage(int argc, char **argv) * "0-9,100-109" instead of a continuous "0-109", and to * state what those two subranges respectively mean. */ printf("%s [,level=<0-9,100-109>]\t0-9=normal, 100-109=extreme (default=%i)\n", - spaces, s->c->default_level); + spaces, s->c->default_level); else printf("%s [,level=<0-%i>]\t\t(default=%i)\n", - spaces, s->c->best_level, s->c->default_level); + spaces, s->c->best_level, s->c->default_level); } if (s->c->setdictsize) { if (s->c->default_dictsize) printf("%s [,dictsize=<dictsize>]\t(default=%u, max=%u)\n", - spaces, s->c->default_dictsize, s->c->max_dictsize); + spaces, s->c->default_dictsize, s->c->max_dictsize); else printf("%s [,dictsize=<dictsize>]\t(default=<auto>, max=%u)\n", - spaces, s->c->max_dictsize); + spaces, s->c->max_dictsize); } } printf( @@ -272,7 +272,7 @@ static struct erofs_s3 s3cfg; #endif #ifdef OCIEROFS_ENABLED -static struct erofs_oci ocicfg; +static struct ocierofs_config ocicfg; static char *mkfs_oci_options; #endif @@ -689,21 +689,14 @@ static int mkfs_parse_s3_cfg(char *cfg_str) #endif #ifdef OCIEROFS_ENABLED - - -/** - * mkfs_parse_oci_options - Parse comma-separated OCI options string +/* + * mkfs_parse_oci_options - Parse OCI options for mkfs tool + * @cfg: OCI configuration structure to update * @options_str: comma-separated options string - * - * Parse OCI options string containing comma-separated key=value pairs. - * Supported options include platform, layer, username, and password. - * - * Return: 0 on success, negative errno on failure */ -static int mkfs_parse_oci_options(char *options_str) +static int mkfs_parse_oci_options(struct ocierofs_config *oci_cfg, char *options_str) { char *opt, *q, *p; - int ret; if (!options_str) return 0; @@ -717,41 +710,38 @@ static int mkfs_parse_oci_options(char *options_str) p = strstr(opt, "platform="); if (p) { p += strlen("platform="); - ret = erofs_oci_params_set_string(&ocicfg.params.platform, p); - if (ret) { - erofs_err("failed to set platform"); - return ret; - } + free(oci_cfg->platform); + oci_cfg->platform = strdup(p); + if (!oci_cfg->platform) + return -ENOMEM; } else { p = strstr(opt, "layer="); if (p) { p += strlen("layer="); - ocicfg.params.layer_index = atoi(p); - if (ocicfg.params.layer_index < 0) { + oci_cfg->layer_index = atoi(p); + if (oci_cfg->layer_index < 0) { erofs_err("invalid layer index %d", - ocicfg.params.layer_index); + oci_cfg->layer_index); return -EINVAL; } } else { p = strstr(opt, "username="); if (p) { p += strlen("username="); - ret = erofs_oci_params_set_string(&ocicfg.params.username, p); - if (ret) { - erofs_err("failed to set username"); - return ret; - } + 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="); - ret = erofs_oci_params_set_string(&ocicfg.params.password, p); - if (ret) { - erofs_err("failed to set password"); - return ret; - } + free(oci_cfg->password); + oci_cfg->password = strdup(p); + if (!oci_cfg->password) + return -ENOMEM; } else { - erofs_err("invalid --oci value %s", opt); + erofs_err("mkfs: invalid --oci value %s", opt); return -EINVAL; } } @@ -763,125 +753,6 @@ static int mkfs_parse_oci_options(char *options_str) return 0; } - -/** - * mkfs_parse_oci_ref - Parse OCI image reference string - * @ref_str: OCI image reference in various formats - * - * Parse OCI image reference which can be in formats: - * - registry.example.com/namespace/repo:tag - * - namespace/repo:tag (uses default registry) - * - repo:tag (adds library/ prefix for Docker Hub) - * - repo (uses default tag "latest") - * - * Return: 0 on success, negative errno on failure - */ -static int mkfs_parse_oci_ref(const char *ref_str) -{ - char *slash, *colon, *dot; - const char *repo_part; - size_t len; - - slash = strchr(ref_str, '/'); - if (slash) { - dot = strchr(ref_str, '.'); - if (dot && dot < slash) { - char *registry_str; - - len = slash - ref_str; - registry_str = strndup(ref_str, len); - - if (!registry_str) { - erofs_err("failed to allocate memory for registry"); - return -ENOMEM; - } - if (erofs_oci_params_set_string(&ocicfg.params.registry, - registry_str)) { - free(registry_str); - erofs_err("failed to set registry"); - return -ENOMEM; - } - free(registry_str); - repo_part = slash + 1; - } else { - repo_part = ref_str; - } - } else { - repo_part = ref_str; - } - - colon = strchr(repo_part, ':'); - if (colon) { - char *repo_str; - - len = colon - repo_part; - repo_str = strndup(repo_part, len); - - if (!repo_str) { - erofs_err("failed to allocate memory for repository"); - return -ENOMEM; - } - - if (!strchr(repo_str, '/') && - (!strcmp(ocicfg.params.registry, DOCKER_API_REGISTRY) || - !strcmp(ocicfg.params.registry, DOCKER_REGISTRY))) { - char *full_repo; - - if (asprintf(&full_repo, "library/%s", repo_str) == -1) { - free(repo_str); - erofs_err("failed to allocate memory for full repository name"); - return -ENOMEM; - } - free(repo_str); - repo_str = full_repo; - } - - if (erofs_oci_params_set_string(&ocicfg.params.repository, - repo_str)) { - free(repo_str); - erofs_err("failed to set repository"); - return -ENOMEM; - } - free(repo_str); - - if (erofs_oci_params_set_string(&ocicfg.params.tag, - colon + 1)) { - erofs_err("failed to set tag"); - return -ENOMEM; - } - } else { - char *repo_str = strdup(repo_part); - - if (!repo_str) { - erofs_err("failed to allocate memory for repository"); - return -ENOMEM; - } - - if (!strchr(repo_str, '/') && - (!strcmp(ocicfg.params.registry, DOCKER_API_REGISTRY) || - !strcmp(ocicfg.params.registry, DOCKER_REGISTRY))) { - char *full_repo; - - if (asprintf(&full_repo, "library/%s", repo_str) == -1) { - free(repo_str); - erofs_err("failed to allocate memory for full repository name"); - return -ENOMEM; - } - free(repo_str); - repo_str = full_repo; - } - - if (erofs_oci_params_set_string(&ocicfg.params.repository, - repo_str)) { - free(repo_str); - erofs_err("failed to set repository"); - return -ENOMEM; - } - free(repo_str); - } - - return 0; -} #endif static int mkfs_parse_one_compress_alg(char *alg, @@ -1951,16 +1822,12 @@ int main(int argc, char **argv) #endif #ifdef OCIEROFS_ENABLED } else if (source_mode == EROFS_MKFS_SOURCE_OCI) { - err = ocierofs_init(&ocicfg); - if (err) - goto exit; + ocicfg.layer_index = -1; - err = mkfs_parse_oci_options(mkfs_oci_options); - if (err) - goto exit; - err = mkfs_parse_oci_ref(cfg.c_src_path); + err = mkfs_parse_oci_options(&ocicfg, mkfs_oci_options); if (err) goto exit; + ocicfg.image_ref = cfg.c_src_path; if (incremental_mode || dataimport_mode == EROFS_MKFS_DATA_IMPORT_RVSP || @@ -2035,9 +1902,6 @@ exit: erofs_blob_exit(); erofs_xattr_cleanup_name_prefixes(); erofs_rebuild_cleanup(); -#ifdef OCIEROFS_ENABLED - ocierofs_cleanup(&ocicfg); -#endif erofs_diskbuf_exit(); if (source_mode == EROFS_MKFS_SOURCE_TAR) { erofs_iostream_close(&erofstar.ios); -- 2.51.0