From: Chengyu Zhu <[email protected]>

Avoid buffering downloaded OCI blobs in memory. Instead, write the
received data directly to the target file descriptor within the curl
write callback.

Signed-off-by: Chengyu Zhu <[email protected]>
---
 lib/remotes/oci.c | 198 +++++++++++++---------------------------------
 1 file changed, 55 insertions(+), 143 deletions(-)

diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c
index 8b253a3..a3efd77 100644
--- a/lib/remotes/oci.c
+++ b/lib/remotes/oci.c
@@ -64,10 +64,8 @@ struct ocierofs_response {
        long http_code;
 };
 
-struct ocierofs_stream {
-       const char *digest;
-       int blobfd;
-};
+static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, const char 
*digest,
+                                       off_t offset, size_t length, int fd);
 
 static inline const char *ocierofs_get_api_registry(const char *registry)
 {
@@ -125,26 +123,42 @@ static size_t ocierofs_write_callback(void *contents, 
size_t size,
        return realsize;
 }
 
-static size_t ocierofs_layer_write_callback(void *contents, size_t size,
-                                           size_t nmemb, void *userp)
+struct ocierofs_write_ctx {
+       int fd;
+       off_t offset;
+       CURL *curl;
+       bool range_req;
+};
+
+static size_t ocierofs_fd_write_callback(void *contents, size_t size,
+                                        size_t nmemb, void *userp)
 {
-       struct ocierofs_stream *stream = userp;
        size_t realsize = size * nmemb;
+       struct ocierofs_write_ctx *wctx = userp;
        const char *buf = contents;
        size_t written = 0;
+       long http_code = 0;
 
-       if (stream->blobfd < 0)
-               return 0;
+       if (wctx->curl) {
+               curl_easy_getinfo(wctx->curl, CURLINFO_RESPONSE_CODE, 
&http_code);
+               if (wctx->range_req && http_code == 200) {
+                       erofs_err("server returned 200 OK for Range request, 
aborting");
+                       return 0;
+               }
+               wctx->curl = NULL;
+       }
 
        while (written < realsize) {
-               ssize_t n = write(stream->blobfd, buf + written, realsize - 
written);
+               ssize_t n = pwrite(wctx->fd, buf + written, realsize - written, 
wctx->offset);
 
                if (n < 0) {
-                       erofs_err("failed to write layer data for layer %s",
-                                 stream->digest);
+                       if (errno == EINTR)
+                               continue;
+                       erofs_err("failed to write cache data: %s", 
strerror(errno));
                        return 0;
                }
                written += n;
+               wctx->offset += n;
        }
        return realsize;
 }
@@ -1157,89 +1171,33 @@ static int ocierofs_init(struct ocierofs_ctx *ctx, 
const struct ocierofs_config
        return 0;
 }
 
-static int ocierofs_download_blob_to_fd(struct ocierofs_ctx *ctx,
-                                       const char *digest,
-                                       const char *auth_header,
-                                       int outfd)
-{
-       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,
-       };
-
-       api_registry = ocierofs_get_api_registry(ctx->registry);
-       if (asprintf(&req.url, "https://%s/v2/%s/blobs/%s";,
-            api_registry, ctx->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->curl);
-
-       ret = ocierofs_curl_setup_common_options(ctx->curl);
-       if (ret)
-               goto out;
-
-       ret = ocierofs_curl_setup_rq(ctx->curl, req.url, OCIEROFS_HTTP_GET,
-                                    req.headers,
-                                    ocierofs_layer_write_callback,
-                                    &stream, NULL, NULL);
-       if (ret)
-               goto out;
-
-       ret = ocierofs_curl_perform(ctx->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;
+       int fd, ret;
 
-       stream = (struct ocierofs_stream) {
-               .digest = digest,
-               .blobfd = erofs_tmpfile(),
-       };
-       if (stream.blobfd < 0) {
+       fd = erofs_tmpfile();
+       if (fd < 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);
+       ret = ocierofs_download_blob_range(ctx, digest, 0, 0, fd);
        if (ret)
                goto out;
 
-       if (lseek(stream.blobfd, 0, SEEK_SET) < 0) {
+       if (lseek(fd, 0, SEEK_SET) < 0) {
                erofs_err("failed to seek to beginning of temp file: %s",
                          strerror(errno));
                ret = -errno;
                goto out;
        }
 
-       return stream.blobfd;
+       return fd;
 
 out:
-       if (stream.blobfd >= 0)
-               close(stream.blobfd);
+       if (fd >= 0)
+               close(fd);
        return ret;
 }
 
@@ -1334,33 +1292,32 @@ out:
        return ret;
 }
 
-static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, off_t 
offset, size_t length,
-                                       void **out_buf, size_t *out_size)
+static int ocierofs_download_blob_range(struct ocierofs_ctx *ctx, const char 
*digest,
+                                       off_t offset, size_t length, int fd)
 {
        struct ocierofs_request req = {};
-       struct ocierofs_response resp = {};
+       struct ocierofs_write_ctx wctx = {
+               .fd = fd,
+               .offset = offset,
+               .curl = ctx->curl,
+               .range_req = (length != 0),
+       };
        const char *api_registry;
        char rangehdr[64];
        long http_code = 0;
        int ret, index;
-       const char *digest;
        u64 blob_size;
-       size_t available;
-       size_t copy_size;
 
-       index = ocierofs_find_layer_by_digest(ctx, ctx->blob_digest);
+       index = ocierofs_find_layer_by_digest(ctx, digest);
        if (index < 0)
                return -ENOENT;
-       digest = ctx->blob_digest;
        blob_size = ctx->layers[index]->size;
 
        if (offset < 0)
                return -EINVAL;
 
-       if (offset >= blob_size) {
-               *out_size = 0;
+       if (offset >= blob_size)
                return 0;
-       }
 
        if (length && offset + length > blob_size)
                length = (size_t)(blob_size - offset);
@@ -1373,13 +1330,15 @@ static int ocierofs_download_blob_range(struct 
ocierofs_ctx *ctx, off_t offset,
        if (length)
                snprintf(rangehdr, sizeof(rangehdr), "Range: bytes=%lld-%lld",
                         (long long)offset, (long long)(offset + (off_t)length 
- 1));
-       else
+       else if (offset > 0)
                snprintf(rangehdr, sizeof(rangehdr), "Range: bytes=%lld-",
                         (long long)offset);
 
        if (ctx->auth_header && strstr(ctx->auth_header, "Bearer"))
                req.headers = curl_slist_append(req.headers, ctx->auth_header);
-       req.headers = curl_slist_append(req.headers, rangehdr);
+
+       if (wctx.range_req || offset > 0)
+               req.headers = curl_slist_append(req.headers, rangehdr);
 
        curl_easy_reset(ctx->curl);
 
@@ -1389,8 +1348,8 @@ static int ocierofs_download_blob_range(struct 
ocierofs_ctx *ctx, off_t offset,
 
        ret = ocierofs_curl_setup_rq(ctx->curl, req.url, OCIEROFS_HTTP_GET,
                                     req.headers,
-                                    ocierofs_write_callback,
-                                    &resp, NULL, NULL);
+                                    ocierofs_fd_write_callback,
+                                    &wctx, NULL, NULL);
        if (ret)
                goto out;
 
@@ -1398,30 +1357,10 @@ static int ocierofs_download_blob_range(struct 
ocierofs_ctx *ctx, off_t offset,
        if (ret)
                goto out;
 
-       ret = 0;
-       if (http_code == 206) {
-               *out_buf = resp.data;
-               *out_size = resp.size;
-               resp.data = NULL;
-       } else if (http_code == 200) {
-               if (!offset) {
-                       *out_buf = resp.data;
-                       *out_size = resp.size;
-                       resp.data = NULL;
-               } else if (offset < resp.size) {
-                       available = resp.size - offset;
-                       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;
-               }
+       if (http_code == 206 || (http_code == 200 && !wctx.range_req && offset 
== 0)) {
+               ret = 0;
        } else {
-               erofs_err("HTTP range request failed with code %ld", http_code);
+               erofs_err("HTTP request failed with code %ld", http_code);
                ret = -EIO;
        }
 
@@ -1429,15 +1368,12 @@ out:
        if (req.headers)
                curl_slist_free_all(req.headers);
        free(req.url);
-       free(resp.data);
        return ret;
 }
 
 static int ocierofs_cache(struct ocierofs_iostream *oci_iostream, off_t 
offset, size_t needed)
 {
        struct ocierofs_ctx *ctx = oci_iostream->ctx;
-       void *download_buf = NULL;
-       size_t download_size = 0;
        int ret = 0;
        off_t hole, align_offset;
        size_t download_len;
@@ -1479,32 +1415,8 @@ static int ocierofs_cache(struct ocierofs_iostream 
*oci_iostream, off_t offset,
        align_offset = round_down(hole, OCIEROFS_IO_CHUNK_SIZE);
        download_len = max_t(size_t, offset + needed - align_offset, 
OCIEROFS_IO_CHUNK_SIZE);
 
-       ret = ocierofs_download_blob_range(ctx, align_offset, download_len,
-                                          &download_buf, &download_size);
-       if (ret < 0)
-               return ret;
-
-       if (download_buf && download_size > 0) {
-               char *p = download_buf;
-               size_t to_write = download_size;
-               ssize_t written = 0;
-
-               while (to_write > 0) {
-                       ssize_t w = pwrite(oci_iostream->cache_fd, p, to_write, 
align_offset + written);
-                       if (w < 0) {
-                               if (errno == EINTR)
-                                       continue;
-                               ret = -errno;
-                               goto out_free;
-                       }
-                       written += w;
-                       p += w;
-                       to_write -= w;
-               }
-       }
-
-out_free:
-       free(download_buf);
+       ret = ocierofs_download_blob_range(ctx, ctx->blob_digest, align_offset, 
download_len,
+                                          oci_iostream->cache_fd);
        return ret;
 }
 
-- 
2.47.1


Reply via email to