Signed-off-by: Christian Couder <chrisc...@tuxfamily.org>
---
 odb-helper.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 78 insertions(+), 6 deletions(-)

diff --git a/odb-helper.c b/odb-helper.c
index 0017faa36e..a27208463c 100644
--- a/odb-helper.c
+++ b/odb-helper.c
@@ -142,6 +142,82 @@ static int check_object_process_error(int err,
        return err;
 }
 
+static ssize_t read_packetized_git_object_to_fd(struct odb_helper *o,
+                                               const unsigned char *sha1,
+                                               int fd_in, int fd_out)
+{
+       ssize_t total_read = 0;
+       unsigned long total_got = 0;
+       int packet_len;
+       git_zstream stream;
+       int zret = Z_STREAM_END;
+       git_SHA_CTX hash;
+       unsigned char real_sha1[20];
+
+       memset(&stream, 0, sizeof(stream));
+       git_inflate_init(&stream);
+       git_SHA1_Init(&hash);
+
+       for (;;) {
+               /* packet_read() writes a '\0' extra byte at the end */
+               char buf[LARGE_PACKET_DATA_MAX + 1];
+
+               packet_len = packet_read(fd_in, NULL, NULL,
+                       buf, LARGE_PACKET_DATA_MAX + 1,
+                       PACKET_READ_GENTLE_ON_EOF);
+
+               if (packet_len <= 0)
+                       break;
+
+               write_or_die(fd_out, buf, packet_len);
+
+               stream.next_in = (unsigned char *)buf;
+               stream.avail_in = packet_len;
+               do {
+                       unsigned char inflated[4096];
+                       unsigned long got;
+
+                       stream.next_out = inflated;
+                       stream.avail_out = sizeof(inflated);
+                       zret = git_inflate(&stream, Z_SYNC_FLUSH);
+                       got = sizeof(inflated) - stream.avail_out;
+
+                       git_SHA1_Update(&hash, inflated, got);
+                       /* skip header when counting size */
+                       if (!total_got) {
+                               const unsigned char *p = memchr(inflated, '\0', 
got);
+                               if (p)
+                                       got -= p - inflated + 1;
+                               else
+                                       got = 0;
+                       }
+                       total_got += got;
+               } while (stream.avail_in && zret == Z_OK);
+
+               total_read += packet_len;
+       }
+
+       git_inflate_end(&stream);
+
+       if (packet_len < 0)
+               return packet_len;
+
+       git_SHA1_Final(real_sha1, &hash);
+
+       if (zret != Z_STREAM_END) {
+               warning("bad zlib data from odb helper '%s' for %s",
+                       o->name, sha1_to_hex(sha1));
+               return -1;
+       }
+       if (hashcmp(real_sha1, sha1)) {
+               warning("sha1 mismatch from odb helper '%s' for %s (got %s)",
+                       o->name, sha1_to_hex(sha1), sha1_to_hex(real_sha1));
+               return -1;
+       }
+
+       return total_read;
+}
+
 static int read_object_process(struct odb_helper *o, const unsigned char 
*sha1, int fd)
 {
        int err;
@@ -174,12 +250,8 @@ static int read_object_process(struct odb_helper *o, const 
unsigned char *sha1,
        if (err)
                goto done;
 
-       if (o->fetch_kind != ODB_FETCH_KIND_FAULT_IN) {
-               struct strbuf buf;
-               read_packetized_to_strbuf(process->out, &buf);
-               if (err)
-                       goto done;
-       }
+       if (o->fetch_kind != ODB_FETCH_KIND_FAULT_IN)
+               err = read_packetized_git_object_to_fd(o, sha1, process->out, 
fd) < 0;
 
        subprocess_read_status(process->out, &status);
        err = strcmp(status.buf, "success");
-- 
2.13.1.565.gbfcd7a9048

Reply via email to