From: Chengyu Zhu <hudson...@tencent.com> - Add OCI blob range download support for efficient data access - Implement `erofs_oci_iostream` for virtual file operations on OCI layers - Support mounting multiple OCI layers using overlayfs - Add --oci option for OCI image mounting with NBD backend
New mount.erofs -t erofs.nbd option: --oci=[option] source-image mountpoint Supported options: - platform=os/arch (default: linux/amd64) - layer=N (extract specific layer, default: all layers) - username/password (basic authentication) e.g.: ./mount/mount.erofs -t erofs.nbd --oci=platform=linux/amd64 \ quay.io/chengyuzhu6/golang:1.22.8-erofs /tmp/test/ This enables mounting EROFS container images directly from OCI registries using NBD as the transport layer, supporting both single and multi-layer images with overlay filesystem composition. Signed-off-by: Chengyu Zhu <hudson...@tencent.com> --- lib/liberofs_oci.h | 64 +++++++ lib/remotes/oci.c | 245 +++++++++++++++++++++++++ mount/Makefile.am | 2 +- mount/main.c | 448 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 758 insertions(+), 1 deletion(-) diff --git a/lib/liberofs_oci.h b/lib/liberofs_oci.h index 34d531c..abad508 100644 --- a/lib/liberofs_oci.h +++ b/lib/liberofs_oci.h @@ -11,6 +11,9 @@ #define DOCKER_REGISTRY "docker.io" #define DOCKER_API_REGISTRY "registry-1.docker.io" +/* Erofs Native Layer Media Type */ +#define EROFS_MEDIATYPE "application/vnd.erofs" + #ifdef __cplusplus extern "C" { #endif @@ -94,6 +97,26 @@ struct erofs_oci { struct erofs_oci_params params; }; +/* + * struct erofs_oci_iostream + * @oci: OCI client (set by caller) + * @vf: Virtual file interface + * @oci_ref: OCI image reference + * @auth_header: Authentication header (set by caller) + * @layer_size: Layer size in bytes + * @current_offset: Current read position + * @initialized: Initialization flag + */ +struct erofs_oci_iostream { + struct erofs_oci *oci; + struct erofs_vfile vf; + char *oci_ref; + char *auth_header; + u64 layer_size; + u64 current_offset; + bool initialized; +}; + /* * ocierofs_init - Initialize OCI client with default parameters * @oci: OCI client structure to initialize @@ -199,6 +222,47 @@ int ocierofs_prepare_auth(struct erofs_oci *oci, char **auth_header_out, */ int ocierofs_curl_clear_auth(struct erofs_oci *oci); + +/* + * ocierofs_download_blob_range - Download a range of a blob into memory + * @oci: OCI client structure + * @digest: blob digest (e.g. sha256:...) + * @auth_header: optional Authorization header (Bearer ...) + * @offset: starting byte offset (>= 0) + * @length: number of bytes to fetch; if 0, fetch to the end + * @out_buf: returns malloc'ed buffer on success (caller frees) + * @out_size: returns size of the buffer + * + * Download a range of bytes from an OCI blob into memory. + * This function supports HTTP range requests for efficient partial downloads. + * + * Return: 0 on success, negative errno on failure + */ +int ocierofs_download_blob_range(struct erofs_oci *oci, + const char *digest, + const char *auth_header, + off_t offset, size_t length, + void **out_buf, size_t *out_size); + +/* + * ocierofs_iostream_init + * @oci_iostream_ptr: pointer to erofs_oci_iostream structure + * @oci_ref: OCI image reference (digest) + * @layer_size: known layer size in bytes + * + * Return: 0 on success, negative errno on failure + */ +int ocierofs_iostream_init(void *oci_iostream_ptr, const char *oci_ref, u64 layer_size); + +/* + * ocierofs_iostream_cleanup + * @oci_iostream_ptr: pointer to erofs_oci_iostream structure + * + * Clean up resources associated with an erofs_oci_iostream structure. + */ +void ocierofs_iostream_cleanup(void *oci_iostream_ptr); + + #ifdef __cplusplus } #endif diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c index 9fc6bf2..914a000 100644 --- a/lib/remotes/oci.c +++ b/lib/remotes/oci.c @@ -1275,3 +1275,248 @@ void ocierofs_free_image_ctx(struct ocierofs_image_context *ctx) ctx->start_index = 0; ctx->using_basic = false; } + +int ocierofs_download_blob_range(struct erofs_oci *oci, + const char *digest, + const char *auth_header, + off_t offset, size_t length, + void **out_buf, size_t *out_size) +{ + struct erofs_oci_request req = {}; + struct erofs_oci_response resp = {}; + const char *api_registry; + char rangehdr[64]; + long http_code = 0; + int ret; + + if (!out_buf || !out_size || !digest || !oci) + return -EINVAL; + *out_buf = NULL; + *out_size = 0; + if (offset < 0) + return -EINVAL; + + 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) + return -ENOMEM; + + if (length) + snprintf(rangehdr, sizeof(rangehdr), "Range: bytes=%lld-%lld", + (long long)offset, (long long)(offset + (off_t)length - 1)); + else + snprintf(rangehdr, sizeof(rangehdr), "Range: bytes=%lld-", + (long long)offset); + + if (auth_header && strstr(auth_header, "Bearer")) + req.headers = curl_slist_append(req.headers, auth_header); + req.headers = curl_slist_append(req.headers, rangehdr); + + 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_write_callback, + &resp, NULL, NULL); + if (ret) + goto out; + + ret = ocierofs_curl_perform(oci->curl, &http_code); + if (ret) + goto out; + + if (http_code == 206) { + *out_buf = resp.data; + *out_size = resp.size; + resp.data = NULL; + ret = 0; + } else if (http_code == 200) { + if (offset == 0) { + *out_buf = resp.data; + *out_size = resp.size; + resp.data = NULL; + ret = 0; + } else { + if (offset < resp.size) { + size_t available = resp.size - offset; + size_t copy_size = length ? min_t(size_t, length, available) : available; + + *out_buf = malloc(copy_size); + if (!*out_buf) { + ret = -ENOMEM; + goto out; + } + memcpy(*out_buf, resp.data + offset, copy_size); + *out_size = copy_size; + ret = 0; + } else { + *out_buf = NULL; + *out_size = 0; + ret = 0; + } + } + } else { + erofs_err("HTTP range request failed with code %ld", http_code); + ret = -EIO; + } + +out: + if (req.headers) + curl_slist_free_all(req.headers); + free(req.url); + free(resp.data); + return ret; +} + +static ssize_t erofs_oci_io_pread(struct erofs_vfile *vf, void *buf, size_t len, u64 offset) +{ + struct erofs_oci_iostream *oci_iostream = *(struct erofs_oci_iostream **)vf->payload; + void *download_buf = NULL; + size_t download_size = 0; + ssize_t ret; + + ret = ocierofs_download_blob_range(oci_iostream->oci, oci_iostream->oci_ref, + oci_iostream->auth_header, offset, len, + &download_buf, &download_size); + if (ret < 0) { + memset(buf, 0, len); + return len; + } + + if (download_buf && download_size > 0) { + memcpy(buf, download_buf, download_size); + free(download_buf); + return download_size; + } + + return 0; +} + +static ssize_t erofs_oci_io_read(struct erofs_vfile *vf, void *buf, size_t len) +{ + struct erofs_oci_iostream *oci_iostream = *(struct erofs_oci_iostream **)vf->payload; + ssize_t ret; + + ret = erofs_oci_io_pread(vf, buf, len, oci_iostream->current_offset); + if (ret > 0) + oci_iostream->current_offset += ret; + + return ret; +} + +static off_t erofs_oci_io_lseek(struct erofs_vfile *vf, u64 offset, int whence) +{ + struct erofs_oci_iostream *oci_iostream = *(struct erofs_oci_iostream **)vf->payload; + off_t new_offset; + + switch (whence) { + case SEEK_SET: + new_offset = offset; + break; + case SEEK_CUR: + new_offset = oci_iostream->current_offset + offset; + break; + case SEEK_END: + new_offset = oci_iostream->layer_size + offset; + break; + default: + return -1; + } + + if (new_offset < 0 || new_offset > oci_iostream->layer_size) + return -1; + + oci_iostream->current_offset = new_offset; + return new_offset; +} + +static ssize_t erofs_oci_io_sendfile(struct erofs_vfile *vout, struct erofs_vfile *vin, + off_t *pos, size_t count) +{ + struct erofs_oci_iostream *oci_iostream = *(struct erofs_oci_iostream **)vin->payload; + char *buf = NULL; + ssize_t total_written = 0; + ssize_t ret = 0; + + buf = malloc(min_t(size_t, count, 32768)); + if (!buf) + return -ENOMEM; + + while (count > 0) { + size_t to_read = min_t(size_t, count, 32768); + u64 read_offset = pos ? *pos : oci_iostream->current_offset; + + ret = erofs_oci_io_pread(vin, buf, to_read, read_offset); + if (ret <= 0) { + erofs_err("OCI I/O sendfile: failed to read from OCI: %s", + erofs_strerror(ret)); + memset(buf, 0, to_read); + ret = to_read; + } + + ssize_t written = write(vout->fd, buf, ret); + + if (written < 0) { + erofs_err("OCI I/O sendfile: failed to write to output: %s", + strerror(errno)); + ret = -errno; + break; + } + + if (written != ret) { + erofs_err("OCI I/O sendfile: partial write: %zd != %zd", written, ret); + ret = written; + } + + total_written += ret; + count -= ret; + if (pos) + *pos += ret; + else + oci_iostream->current_offset += ret; + } + + free(buf); + return count; +} + +static struct erofs_vfops erofs_oci_io_vfops = { + .pread = erofs_oci_io_pread, + .read = erofs_oci_io_read, + .lseek = erofs_oci_io_lseek, + .sendfile = erofs_oci_io_sendfile, +}; + +int ocierofs_iostream_init(void *oci_iostream_ptr, const char *oci_ref, u64 layer_size) +{ + struct erofs_oci_iostream *oci_iostream = (struct erofs_oci_iostream *)oci_iostream_ptr; + + memset(oci_iostream, 0, sizeof(*oci_iostream)); + oci_iostream->oci_ref = strdup(oci_ref); + if (!oci_iostream->oci_ref) + return -ENOMEM; + + oci_iostream->layer_size = layer_size; + oci_iostream->vf.ops = &erofs_oci_io_vfops; + oci_iostream->vf.fd = -1; + *(struct erofs_oci_iostream **)oci_iostream->vf.payload = oci_iostream; + oci_iostream->initialized = true; + + return 0; +} + +void ocierofs_iostream_cleanup(void *oci_iostream_ptr) +{ + struct erofs_oci_iostream *oci_iostream = (struct erofs_oci_iostream *)oci_iostream_ptr; + + free(oci_iostream->auth_header); + oci_iostream->auth_header = NULL; + free(oci_iostream->oci_ref); + oci_iostream->oci_ref = NULL; + oci_iostream->initialized = false; +} diff --git a/mount/Makefile.am b/mount/Makefile.am index b76e336..b64059d 100644 --- a/mount/Makefile.am +++ b/mount/Makefile.am @@ -9,5 +9,5 @@ mount_erofs_SOURCES = main.c mount_erofs_CFLAGS = -Wall -I$(top_srcdir)/include mount_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \ ${liblz4_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libdeflate_LIBS} \ - ${libzstd_LIBS} ${libqpl_LIBS} ${libxxhash_LIBS} + ${libzstd_LIBS} ${libqpl_LIBS} ${libxxhash_LIBS} ${openssl_LIBS} endif diff --git a/mount/main.c b/mount/main.c index c9deae2..fe48da2 100644 --- a/mount/main.c +++ b/mount/main.c @@ -14,6 +14,7 @@ #include "erofs/err.h" #include "erofs/io.h" #include "../lib/liberofs_nbd.h" +#include "../lib/liberofs_oci.h" #ifdef HAVE_LINUX_LOOP_H #include <linux/loop.h> #else @@ -49,6 +50,10 @@ static struct erofsmount_cfg { long flags; enum erofs_backend_drv backend; bool umount; +#ifdef OCIEROFS_ENABLED + char *oci_options; + bool use_oci; +#endif } mountcfg = { .full_options = "ro", .flags = MS_RDONLY, /* default mountflags */ @@ -120,6 +125,9 @@ static int erofsmount_parse_options(int argc, char **argv) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, +#ifdef OCIEROFS_ENABLED + {"oci", optional_argument, 0, 200}, +#endif {0, 0, 0, 0}, }; char *dot; @@ -154,6 +162,12 @@ static int erofsmount_parse_options(int argc, char **argv) case 'u': mountcfg.umount = true; break; +#ifdef OCIEROFS_ENABLED + case 200: + mountcfg.oci_options = optarg; + mountcfg.use_oci = true; + break; +#endif default: return -EINVAL; } @@ -184,6 +198,88 @@ static int erofsmount_parse_options(int argc, char **argv) return 0; } + +/* + * mount_parse_oci_options - Parse OCI options for mount tool + * @oci: OCI client structure to update + * @options_str: comma-separated options string + * + * Parse OCI options string containing comma-separated key=value pairs. + * This function is specific to the mount tool and can be enhanced + * independently of other tools. + * + * Supported options include: + * - platform=<os/arch>: target platform (e.g., "linux/amd64") + * - layer=<index>: specific layer to extract (0-based index) + * - username=<user>: authentication username + * - password=<pass>: authentication password + * + * Return: 0 on success, negative errno on failure + */ +static int mount_parse_oci_options(struct erofs_oci *oci, char *options_str) +{ + char *opt, *q, *p; + int ret; + + if (!options_str) + return 0; + + opt = options_str; + while (opt) { + q = strchr(opt, ','); + if (q) + *q = '\0'; + + p = strstr(opt, "platform="); + if (p) { + p += strlen("platform="); + ret = erofs_oci_params_set_string(&oci->params.platform, p); + if (ret) { + erofs_err("failed to set platform"); + return ret; + } + } else { + p = strstr(opt, "layer="); + if (p) { + p += strlen("layer="); + oci->params.layer_index = atoi(p); + if (oci->params.layer_index < 0) { + erofs_err("invalid layer index %d", + oci->params.layer_index); + return -EINVAL; + } + } else { + p = strstr(opt, "username="); + if (p) { + p += strlen("username="); + ret = erofs_oci_params_set_string(&oci->params.username, p); + if (ret) { + erofs_err("failed to set username"); + return ret; + } + } else { + p = strstr(opt, "password="); + if (p) { + p += strlen("password="); + ret = erofs_oci_params_set_string(&oci->params.password, p); + if (ret) { + erofs_err("failed to set password"); + return ret; + } + } else { + erofs_err("mount: invalid --oci value %s", opt); + return -EINVAL; + } + } + } + } + + opt = q ? q + 1 : NULL; + } + + return 0; +} + static int erofsmount_fuse(const char *source, const char *mountpoint, const char *fstype, const char *options) { @@ -502,6 +598,346 @@ err_out: return err < 0 ? err : 0; } +/** + * erofsmount_startnbd_oci - Start NBD server for OCI image + * @nbdfd: NBD device file descriptor + * @oci_ref: OCI image reference + * @oci: OCI client structure (pre-authenticated) + * @auth_header: pre-authenticated auth header + * + * Start an NBD server that serves data from an OCI image layer. + * This function reuses the existing erofsmount_nbd_loopfn logic + * but uses erofs_oci_iostream as the virtual device instead of a local file. + * The OCI client should be pre-authenticated to avoid concurrent auth issues. + * + * Return: 0 on success, negative errno on failure + */ +static int erofsmount_startnbd_oci(int nbdfd, const char *oci_ref, struct erofs_oci *oci, + const char *auth_header, u64 layer_size) +{ + struct erofsmount_nbd_ctx ctx = {}; + struct erofs_oci_iostream *oci_iostream = NULL; + uintptr_t retcode; + pthread_t th; + int err, err2; + int blkbits = 12; + u64 blocks; + + blocks = (layer_size + (1ULL << blkbits) - 1) >> blkbits; + + oci_iostream = malloc(sizeof(struct erofs_oci_iostream)); + if (!oci_iostream) + return -ENOMEM; + + err = ocierofs_iostream_init(oci_iostream, oci_ref, layer_size); + if (err) { + free(oci_iostream); + return err; + } + + oci_iostream->oci = oci; + if (auth_header) + oci_iostream->auth_header = strdup(auth_header); + + ctx.vd = oci_iostream->vf; + + err = erofs_nbd_connect(nbdfd, blkbits, blocks); + if (err < 0) { + ocierofs_iostream_cleanup(oci_iostream); + free(oci_iostream); + return err; + } + ctx.sk.fd = err; + + err = -pthread_create(&th, NULL, erofsmount_nbd_loopfn, &ctx); + if (err) { + ocierofs_iostream_cleanup(oci_iostream); + free(oci_iostream); + close(ctx.sk.fd); + return err; + } + + err = erofs_nbd_do_it(nbdfd); + err2 = -pthread_join(th, (void **)&retcode); + if (!err2 && retcode) { + erofs_err("NBD worker failed with %s", + erofs_strerror(retcode)); + err2 = retcode; + } + + ocierofs_iostream_cleanup(oci_iostream); + free(oci_iostream); + + return err ?: err2; +} + +/** + * erofsmount_overlay_from_mountpoints - Mount overlayfs from pre-mounted lowerdirs + * @layer_mountpoints: array of mountpoint paths for each layer + * @layer_count: number of layers + * @mountpoint: final mountpoint for the overlay + * @flags: mount flags + * + * Return: 0 on success, negative errno on failure + */ +static int erofsmount_overlay_from_mountpoints(const char **layer_mountpoints, int layer_count, + const char *mountpoint, int flags) +{ + char *workdir = NULL; + char *upperdir = NULL; + char *lowerdirs = NULL; + int i, ret = 0; + + if (!layer_mountpoints || !layer_count || !mountpoint) + return -EINVAL; + + for (i = 0; i < layer_count; i++) { + char *next = NULL; + + if (!lowerdirs) { + if (asprintf(&next, "%s", layer_mountpoints[i]) < 0) { + ret = -ENOMEM; + goto out_free; + } + } else { + if (asprintf(&next, "%s:%s", lowerdirs, layer_mountpoints[i]) < 0) { + ret = -ENOMEM; + goto out_free; + } + free(lowerdirs); + } + lowerdirs = next; + } + + if (asprintf(&workdir, "%s.work", mountpoint) < 0) { + ret = -ENOMEM; + goto out_free; + } + if (asprintf(&upperdir, "%s.upper", mountpoint) < 0) { + ret = -ENOMEM; + goto out_free; + } + + if (mkdir(workdir, 0755) < 0 && errno != EEXIST) { + ret = -errno; + goto out_free; + } + if (mkdir(upperdir, 0755) < 0 && errno != EEXIST) { + ret = -errno; + goto out_free; + } + + { + char *overlay_options = NULL; + + if (asprintf(&overlay_options, + "lowerdir=%s,upperdir=%s,workdir=%s", + lowerdirs, upperdir, workdir) < 0) { + ret = -ENOMEM; + goto out_free; + } + + ret = mount("overlay", mountpoint, "overlay", flags, overlay_options); + if (ret < 0) + ret = -errno; + + free(overlay_options); + } + +out_free: + free(workdir); + free(upperdir); + free(lowerdirs); + return ret; +} + +/** + * erofsmount_nbd_oci_one_layer - Mount single OCI layer using NBD + * @oci_ref: OCI image reference (digest) + * @oci: OCI client structure (pre-authenticated) + * @auth_header: pre-authenticated auth header + * @mountpoint: mount point path + * @fstype: filesystem type + * @flags: mount flags + * @options: mount options + * @layer_size: layer size in bytes + * + * Return: 0 on success, negative errno on failure + */ +static int erofsmount_nbd_oci_one_layer(const char *oci_ref, struct erofs_oci *oci, + const char *auth_header, const char *mountpoint, + const char *fstype, int flags, const char *options, + u64 layer_size) +{ + char nbdpath[32]; + int num, nbdfd; + pid_t pid; + long err; + + if (strcmp(fstype, "erofs")) { + fprintf(stderr, "unsupported filesystem type `%s`\n", fstype); + return -ENODEV; + } + + flags |= MS_RDONLY; + + num = erofs_nbd_devscan(); + if (num < 0) + return num; + + (void)snprintf(nbdpath, sizeof(nbdpath), "/dev/nbd%d", num); + nbdfd = open(nbdpath, O_RDWR); + if (nbdfd < 0) + return -errno; + + if ((pid = fork()) == 0) { + return erofsmount_startnbd_oci(nbdfd, oci_ref, oci, auth_header, layer_size) ? + EXIT_FAILURE : EXIT_SUCCESS; + } + close(nbdfd); + + while (1) { + err = erofs_nbd_in_service(num); + if (err == -ENOENT || err == -ENOTCONN) { + usleep(50000); + continue; + } + if (err >= 0) + err = (err != pid ? -EBUSY : 0); + break; + } + if (!err) { + err = mount(nbdpath, mountpoint, fstype, flags, options); + if (err < 0) + err = -errno; + } + return err; +} + +int erofsmount_nbd_oci(const char *source, const char *mountpoint, + const char *fstype, int flags, + const char *options) +{ + struct erofs_oci oci = {}; + char **layer_mountpoints = NULL; + int i, err; + int mounted_count = 0; + + err = ocierofs_init(&oci); + if (err) + goto exit; + + err = mount_parse_oci_options(&oci, mountcfg.oci_options); + if (err) + goto out_oci_cleanup; + + err = ocierofs_parse_ref(&oci, source); + if (err) + goto out_oci_cleanup; + + struct ocierofs_image_context image_ctx = {}; + + err = ocierofs_prepare_layers(&oci, &image_ctx); + if (err < 0) { + erofs_err("Failed to get EROFS layers information: %s", erofs_strerror(err)); + goto out_oci_cleanup; + } + + if (image_ctx.layer_count > 0 && image_ctx.layers[0] && image_ctx.layers[0]->media_type) { + if (strcmp(image_ctx.layers[0]->media_type, EROFS_MEDIATYPE) != 0) { + err = -ENOENT; + goto out_oci_cleanup; + } + } else { + err = -ENOENT; + goto out_oci_cleanup; + } + + layer_mountpoints = calloc(image_ctx.layer_count, sizeof(char *)); + if (!layer_mountpoints) { + err = -ENOMEM; + goto out_auth_cleanup; + } + + for (i = 0; i < image_ctx.layer_count; i++) { + if (image_ctx.layer_count == 1) { + layer_mountpoints[i] = strdup(mountpoint); + } else { + const char *dg = image_ctx.layers[i]->digest ? image_ctx.layers[i]->digest : ""; + const char *hex = strchr(dg, ':') ? strchr(dg, ':') + 1 : dg; + char dg8[9]; + int n = strlen(hex) < 8 ? (int)strlen(hex) : 8; + + memcpy(dg8, hex, n); + dg8[n] = '\0'; + + if (asprintf(&layer_mountpoints[i], "%s.%s", mountpoint, dg8) == -1) { + err = -ENOMEM; + goto out_cleanup_mounts; + } + } + + if (!layer_mountpoints[i]) { + err = -ENOMEM; + goto out_cleanup_mounts; + } + + if (mkdir(layer_mountpoints[i], 0755) < 0 && errno != EEXIST) { + err = -errno; + goto out_cleanup_mounts; + } + + err = erofsmount_nbd_oci_one_layer(image_ctx.layers[i]->digest, &oci, image_ctx.auth_header, + layer_mountpoints[i], fstype, flags, + options, image_ctx.layers[i]->size); + if (err) + goto out_cleanup_mounts; + mounted_count++; + } + + if (image_ctx.layer_count > 1) { + err = erofsmount_overlay_from_mountpoints((const char **)layer_mountpoints, + image_ctx.layer_count, mountpoint, flags); + if (err) + goto out_cleanup_mounts; + } + + for (i = 0; i < image_ctx.layer_count; i++) { + if (layer_mountpoints[i]) + free(layer_mountpoints[i]); + } + free(layer_mountpoints); + + ocierofs_free_image_ctx(&image_ctx); + + ocierofs_cleanup(&oci); + return 0; + +out_auth_cleanup: + ocierofs_free_image_ctx(&image_ctx); + +out_cleanup_mounts: + if (layer_mountpoints) { + for (i = mounted_count - 1; i >= 0; i--) { + if (layer_mountpoints[i]) + umount(layer_mountpoints[i]); + } + for (i = 0; i < image_ctx.layer_count; i++) { + if (layer_mountpoints[i]) { + rmdir(layer_mountpoints[i]); + free(layer_mountpoints[i]); + } + } + free(layer_mountpoints); + } + +out_oci_cleanup: + ocierofs_cleanup(&oci); +exit: + erofs_err("OCI NBD mount failed with error: %s", erofs_strerror(err)); + return err; +} + int main(int argc, char *argv[]) { int err; @@ -529,9 +965,21 @@ int main(int argc, char *argv[]) } if (mountcfg.backend == EROFSNBD) { +#ifdef OCIEROFS_ENABLED + if (mountcfg.use_oci) { + err = erofsmount_nbd_oci(mountcfg.device, mountcfg.mountpoint, + mountcfg.fstype, mountcfg.flags, + mountcfg.options); + } else { + err = erofsmount_nbd(mountcfg.device, mountcfg.mountpoint, + mountcfg.fstype, mountcfg.flags, + mountcfg.options); + } +#else err = erofsmount_nbd(mountcfg.device, mountcfg.mountpoint, mountcfg.fstype, mountcfg.flags, mountcfg.options); +#endif goto exit; } -- 2.51.0