sha1_file: support reading from a loose object of unknown type

Update sha1_loose_object_info() to optionally allow it to read from a loose object file of unknown/bogus type; as the function usually returns the type of the object it read in the form of enum for known types, add an optional "typename" field to receive the name of the type in textual form and a flag to indicate the reading of a loose object file of unknown/bogus type. Add parse_sha1_header_extended() which acts as a wrapper around parse_sha1_header() allowing more information to be obtained. Add unpack_sha1_header_to_strbuf() to unpack sha1 headers of unknown/corrupt objects which have a unknown sha1 header size to a strbuf structure. This was written by Junio C Hamano but tested by me. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Helped-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk> Hepled-by: Jeff King <peff@peff.net> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed May 3, 2015 at 19:59 UTC 46f034483eb9f88cf79cba8b76f085464287fc58
2 files changed +108 -22
cache.h
+2
@@ -838,6 +838,7 @@ extern int is_ntfs_dotgit(const char *name);
838
839 /* object replacement */
840 #define LOOKUP_REPLACE_OBJECT 1
841 +#define LOOKUP_UNKNOWN_OBJECT 2
842 extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
843 static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
844 {
@@ -1304,6 +1305,7 @@ struct object_info {
1305 unsigned long *sizep;
1306 unsigned long *disk_sizep;
1307 unsigned char *delta_base_sha1;
1308 + struct strbuf *typename;
1309
1310 /* Response */
1311 enum {
sha1_file.c
+106 -22
@@ -1564,6 +1564,40 @@ int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long ma
1564 return git_inflate(stream, 0);
1565 }
1566
1567 +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
1568 + unsigned long mapsize, void *buffer,
1569 + unsigned long bufsiz, struct strbuf *header)
1570 +{
1571 + int status;
1572 +
1573 + status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
1574 +
1575 + /*
1576 + * Check if entire header is unpacked in the first iteration.
1577 + */
1578 + if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
1579 + return 0;
1580 +
1581 + /*
1582 + * buffer[0..bufsiz] was not large enough. Copy the partial
1583 + * result out to header, and then append the result of further
1584 + * reading the stream.
1585 + */
1586 + strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
1587 + stream->next_out = buffer;
1588 + stream->avail_out = bufsiz;
1589 +
1590 + do {
1591 + status = git_inflate(stream, 0);
1592 + strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
1593 + if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
1594 + return 0;
1595 + stream->next_out = buffer;
1596 + stream->avail_out = bufsiz;
1597 + } while (status != Z_STREAM_END);
1598 + return -1;
1599 +}
1600 +
1601 static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
1602 {
1603 int bytes = strlen(buffer) + 1;
@@ -1614,27 +1648,38 @@ static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long s
1648 * too permissive for what we want to check. So do an anal
1649 * object header parse by hand.
1650 */
1617 -int parse_sha1_header(const char *hdr, unsigned long *sizep)
1651 +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
1652 + unsigned int flags)
1653 {
1619 - char type[10];
1620 - int i;
1654 + const char *type_buf = hdr;
1655 unsigned long size;
1656 + int type, type_len = 0;
1657
1658 /*
1624 - * The type can be at most ten bytes (including the
1625 - * terminating '\0' that we add), and is followed by
1659 + * The type can be of any size but is followed by
1660 * a space.
1661 */
1628 - i = 0;
1662 for (;;) {
1663 char c = *hdr++;
1664 if (c == ' ')
1665 break;
1633 - type[i++] = c;
1634 - if (i >= sizeof(type))
1635 - return -1;
1666 + type_len++;
1667 }
1637 - type[i] = 0;
1668 +
1669 + type = type_from_string_gently(type_buf, type_len, 1);
1670 + if (oi->typename)
1671 + strbuf_add(oi->typename, type_buf, type_len);
1672 + /*
1673 + * Set type to 0 if its an unknown object and
1674 + * we're obtaining the type using '--allow-unkown-type'
1675 + * option.
1676 + */
1677 + if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
1678 + type = 0;
1679 + else if (type < 0)
1680 + die("invalid object type");
1681 + if (oi->typep)
1682 + *oi->typep = type;
1683
1684 /*
1685 * The length must follow immediately, and be in canonical
@@ -1652,12 +1697,24 @@ int parse_sha1_header(const char *hdr, unsigned long *sizep)
1697 size = size * 10 + c;
1698 }
1699 }
1655 - *sizep = size;
1700 +
1701 + if (oi->sizep)
1702 + *oi->sizep = size;
1703
1704 /*
1705 * The length must be followed by a zero byte
1706 */
1660 - return *hdr ? -1 : type_from_string(type);
1707 + return *hdr ? -1 : type;
1708 +}
1709 +
1710 +int parse_sha1_header(const char *hdr, unsigned long *sizep)
1711 +{
1712 + struct object_info oi;
1713 +
1714 + oi.sizep = sizep;
1715 + oi.typename = NULL;
1716 + oi.typep = NULL;
1717 + return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
1718 }
1719
1720 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@ -2524,13 +2581,15 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
2581 }
2582
2583 static int sha1_loose_object_info(const unsigned char *sha1,
2527 - struct object_info *oi)
2584 + struct object_info *oi,
2585 + int flags)
2586 {
2529 - int status;
2530 - unsigned long mapsize, size;
2587 + int status = 0;
2588 + unsigned long mapsize;
2589 void *map;
2590 git_zstream stream;
2591 char hdr[32];
2592 + struct strbuf hdrbuf = STRBUF_INIT;
2593
2594 if (oi->delta_base_sha1)
2595 hashclr(oi->delta_base_sha1);
@@ -2543,7 +2602,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,
2602 * return value implicitly indicates whether the
2603 * object even exists.
2604 */
2546 - if (!oi->typep && !oi->sizep) {
2605 + if (!oi->typep && !oi->typename && !oi->sizep) {
2606 struct stat st;
2607 if (stat_sha1_file(sha1, &st) < 0)
2608 return -1;
@@ -2557,17 +2616,26 @@ static int sha1_loose_object_info(const unsigned char *sha1,
2616 return -1;
2617 if (oi->disk_sizep)
2618 *oi->disk_sizep = mapsize;
2560 - if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
2619 + if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
2620 + if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
2621 + status = error("unable to unpack %s header with --allow-unknown-type",
2622 + sha1_to_hex(sha1));
2623 + } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
2624 status = error("unable to unpack %s header",
2625 sha1_to_hex(sha1));
2563 - else if ((status = parse_sha1_header(hdr, &size)) < 0)
2626 + if (status < 0)
2627 + ; /* Do nothing */
2628 + else if (hdrbuf.len) {
2629 + if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
2630 + status = error("unable to parse %s header with --allow-unknown-type",
2631 + sha1_to_hex(sha1));
2632 + } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
2633 status = error("unable to parse %s header", sha1_to_hex(sha1));
2565 - else if (oi->sizep)
2566 - *oi->sizep = size;
2634 git_inflate_end(&stream);
2635 munmap(map, mapsize);
2569 - if (oi->typep)
2636 + if (status && oi->typep)
2637 *oi->typep = status;
2638 + strbuf_release(&hdrbuf);
2639 return 0;
2640 }
2641
@@ -2576,6 +2644,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
2644 struct cached_object *co;
2645 struct pack_entry e;
2646 int rtype;
2647 + enum object_type real_type;
2648 const unsigned char *real = lookup_replace_object_extended(sha1, flags);
2649
2650 co = find_cached_object(real);
@@ -2588,13 +2657,15 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
2657 *(oi->disk_sizep) = 0;
2658 if (oi->delta_base_sha1)
2659 hashclr(oi->delta_base_sha1);
2660 + if (oi->typename)
2661 + strbuf_addstr(oi->typename, typename(co->type));
2662 oi->whence = OI_CACHED;
2663 return 0;
2664 }
2665
2666 if (!find_pack_entry(real, &e)) {
2667 /* Most likely it's a loose object. */
2597 - if (!sha1_loose_object_info(real, oi)) {
2668 + if (!sha1_loose_object_info(real, oi, flags)) {
2669 oi->whence = OI_LOOSE;
2670 return 0;
2671 }
@@ -2605,9 +2676,18 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
2676 return -1;
2677 }
2678
2679 + /*
2680 + * packed_object_info() does not follow the delta chain to
2681 + * find out the real type, unless it is given oi->typep.
2682 + */
2683 + if (oi->typename && !oi->typep)
2684 + oi->typep = &real_type;
2685 +
2686 rtype = packed_object_info(e.p, e.offset, oi);
2687 if (rtype < 0) {
2688 mark_bad_packed_object(e.p, real);
2689 + if (oi->typep == &real_type)
2690 + oi->typep = NULL;
2691 return sha1_object_info_extended(real, oi, 0);
2692 } else if (in_delta_base_cache(e.p, e.offset)) {
2693 oi->whence = OI_DBCACHED;
@@ -2618,6 +2698,10 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi,
2698 oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
2699 rtype == OBJ_OFS_DELTA);
2700 }
2701 + if (oi->typename)
2702 + strbuf_addstr(oi->typename, typename(*oi->typep));
2703 + if (oi->typep == &real_type)
2704 + oi->typep = NULL;
2705
2706 return 0;
2707 }