Signed-off-by: Christian Couder <chrisc...@tuxfamily.org>
---
 odb-helper.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 118 insertions(+), 1 deletion(-)

diff --git a/odb-helper.c b/odb-helper.c
index a27208463c..b2d86a7928 100644
--- a/odb-helper.c
+++ b/odb-helper.c
@@ -142,6 +142,121 @@ static int check_object_process_error(int err,
        return err;
 }
 
+static struct odb_helper_object *odb_helper_lookup(struct odb_helper *o,
+                                                  const unsigned char *sha1);
+
+static ssize_t read_packetized_plain_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;
+
+       char hdr[32];
+       int hdrlen;
+
+       int ret = Z_STREAM_END;
+       unsigned char compressed[4096];
+       git_zstream stream;
+       git_SHA_CTX hash;
+       unsigned char real_sha1[20];
+
+       off_t size;
+       enum object_type type;
+       const char *s;
+       int pkt_size;
+       char *size_buf;
+
+       size_buf = packet_read_line(fd_in, &pkt_size);
+       if (!skip_prefix(size_buf, "size=", &s))
+               return error("odb helper '%s' did not send size of plain 
object", o->name);
+       size = strtoumax(s, NULL, 10);
+       if (!skip_prefix(packet_read_line(fd_in, NULL), "kind=", &s))
+               return error("odb helper '%s' did not send kind of plain 
object", o->name);
+       /* Check if the object is not available */
+       if (!strcmp(s, "none"))
+               return -1;
+       type = type_from_string_gently(s, strlen(s), 1);
+       if (type < 0)
+               return error("odb helper '%s' sent bad type '%s'", o->name, s);
+
+       /* Set it up */
+       git_deflate_init(&stream, zlib_compression_level);
+       stream.next_out = compressed;
+       stream.avail_out = sizeof(compressed);
+       git_SHA1_Init(&hash);
+
+       /* First header.. */
+       hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), size) + 
1;
+       stream.next_in = (unsigned char *)hdr;
+       stream.avail_in = hdrlen;
+       while (git_deflate(&stream, 0) == Z_OK)
+               ; /* nothing */
+       git_SHA1_Update(&hash, hdr, hdrlen);
+
+       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;
+
+               total_got += packet_len;
+
+               /* Then the data itself.. */
+               stream.next_in = (void *)buf;
+               stream.avail_in = packet_len;
+               do {
+                       unsigned char *in0 = stream.next_in;
+                       ret = git_deflate(&stream, Z_FINISH);
+                       git_SHA1_Update(&hash, in0, stream.next_in - in0);
+                       write_or_die(fd_out, compressed, stream.next_out - 
compressed);
+                       stream.next_out = compressed;
+                       stream.avail_out = sizeof(compressed);
+               } while (ret == Z_OK);
+
+               total_read += packet_len;
+       }
+
+       if (packet_len < 0) {
+               error("unable to read from odb helper '%s': %s",
+                     o->name, strerror(errno));
+               git_deflate_end(&stream);
+               return packet_len;
+       }
+
+       if (ret != Z_STREAM_END) {
+               warning("bad zlib data from odb helper '%s' for %s",
+                       o->name, sha1_to_hex(sha1));
+               return -1;
+       }
+
+       ret = git_deflate_end_gently(&stream);
+       if (ret != Z_OK) {
+               warning("deflateEnd on object %s from odb helper '%s' failed 
(%d)",
+                       sha1_to_hex(sha1), o->name, ret);
+               return -1;
+       }
+       git_SHA1_Final(real_sha1, &hash);
+       if (hashcmp(sha1, real_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;
+       }
+       if (total_got != size) {
+               warning("size mismatch from odb helper '%s' for %s (%lu != 
%lu)",
+                       o->name, sha1_to_hex(sha1), total_got, size);
+               return -1;
+       }
+
+       return total_read;
+}
+
 static ssize_t read_packetized_git_object_to_fd(struct odb_helper *o,
                                                const unsigned char *sha1,
                                                int fd_in, int fd_out)
@@ -250,7 +365,9 @@ 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)
+       if (o->fetch_kind == ODB_FETCH_KIND_PLAIN_OBJECT)
+               err = read_packetized_plain_object_to_fd(o, sha1, process->out, 
fd) < 0;
+       else if (o->fetch_kind == ODB_FETCH_KIND_GIT_OBJECT)
                err = read_packetized_git_object_to_fd(o, sha1, process->out, 
fd) < 0;
 
        subprocess_read_status(process->out, &status);
-- 
2.13.1.565.gbfcd7a9048

Reply via email to