Merge branch 'np/pack-safer'
* np/pack-safer: t5303: fix printf format string for portability t5303: work around printf breakage in dash pack-objects: don't leak pack window reference when splitting packs extend test coverage for latest pack corruption resilience improvements pack-objects: allow "fixing" a corrupted pack without a full repack make find_pack_revindex() aware of the nasty world make check_object() resilient to pack corruptions make packed_object_info() resilient to pack corruptions make unpack_object_header() non fatal better validation on delta base object offsets close another possibility for propagating pack corruption
This commit is contained in:
commit
7b51b77dbc
@ -286,6 +286,7 @@ static unsigned long write_object(struct sha1file *f,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (!to_reuse) {
|
if (!to_reuse) {
|
||||||
|
no_reuse:
|
||||||
if (!usable_delta) {
|
if (!usable_delta) {
|
||||||
buf = read_sha1_file(entry->idx.sha1, &type, &size);
|
buf = read_sha1_file(entry->idx.sha1, &type, &size);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
@ -367,46 +368,60 @@ static unsigned long write_object(struct sha1file *f,
|
|||||||
struct revindex_entry *revidx;
|
struct revindex_entry *revidx;
|
||||||
off_t offset;
|
off_t offset;
|
||||||
|
|
||||||
if (entry->delta) {
|
if (entry->delta)
|
||||||
type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
type = (allow_ofs_delta && entry->delta->idx.offset) ?
|
||||||
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
OBJ_OFS_DELTA : OBJ_REF_DELTA;
|
||||||
reused_delta++;
|
|
||||||
}
|
|
||||||
hdrlen = encode_header(type, entry->size, header);
|
hdrlen = encode_header(type, entry->size, header);
|
||||||
|
|
||||||
offset = entry->in_pack_offset;
|
offset = entry->in_pack_offset;
|
||||||
revidx = find_pack_revindex(p, offset);
|
revidx = find_pack_revindex(p, offset);
|
||||||
datalen = revidx[1].offset - offset;
|
datalen = revidx[1].offset - offset;
|
||||||
if (!pack_to_stdout && p->index_version > 1 &&
|
if (!pack_to_stdout && p->index_version > 1 &&
|
||||||
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
|
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
|
||||||
die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
|
error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
|
||||||
|
unuse_pack(&w_curs);
|
||||||
|
goto no_reuse;
|
||||||
|
}
|
||||||
|
|
||||||
offset += entry->in_pack_header_size;
|
offset += entry->in_pack_header_size;
|
||||||
datalen -= entry->in_pack_header_size;
|
datalen -= entry->in_pack_header_size;
|
||||||
|
if (!pack_to_stdout && p->index_version == 1 &&
|
||||||
|
check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
|
||||||
|
error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
|
||||||
|
unuse_pack(&w_curs);
|
||||||
|
goto no_reuse;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == OBJ_OFS_DELTA) {
|
if (type == OBJ_OFS_DELTA) {
|
||||||
off_t ofs = entry->idx.offset - entry->delta->idx.offset;
|
off_t ofs = entry->idx.offset - entry->delta->idx.offset;
|
||||||
unsigned pos = sizeof(dheader) - 1;
|
unsigned pos = sizeof(dheader) - 1;
|
||||||
dheader[pos] = ofs & 127;
|
dheader[pos] = ofs & 127;
|
||||||
while (ofs >>= 7)
|
while (ofs >>= 7)
|
||||||
dheader[--pos] = 128 | (--ofs & 127);
|
dheader[--pos] = 128 | (--ofs & 127);
|
||||||
if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
|
if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
|
||||||
|
unuse_pack(&w_curs);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
sha1write(f, header, hdrlen);
|
sha1write(f, header, hdrlen);
|
||||||
sha1write(f, dheader + pos, sizeof(dheader) - pos);
|
sha1write(f, dheader + pos, sizeof(dheader) - pos);
|
||||||
hdrlen += sizeof(dheader) - pos;
|
hdrlen += sizeof(dheader) - pos;
|
||||||
|
reused_delta++;
|
||||||
} else if (type == OBJ_REF_DELTA) {
|
} else if (type == OBJ_REF_DELTA) {
|
||||||
if (limit && hdrlen + 20 + datalen + 20 >= limit)
|
if (limit && hdrlen + 20 + datalen + 20 >= limit) {
|
||||||
|
unuse_pack(&w_curs);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
sha1write(f, header, hdrlen);
|
sha1write(f, header, hdrlen);
|
||||||
sha1write(f, entry->delta->idx.sha1, 20);
|
sha1write(f, entry->delta->idx.sha1, 20);
|
||||||
hdrlen += 20;
|
hdrlen += 20;
|
||||||
|
reused_delta++;
|
||||||
} else {
|
} else {
|
||||||
if (limit && hdrlen + datalen + 20 >= limit)
|
if (limit && hdrlen + datalen + 20 >= limit) {
|
||||||
|
unuse_pack(&w_curs);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
sha1write(f, header, hdrlen);
|
sha1write(f, header, hdrlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pack_to_stdout && p->index_version == 1 &&
|
|
||||||
check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
|
|
||||||
die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
|
|
||||||
copy_pack_data(f, p, &w_curs, offset, datalen);
|
copy_pack_data(f, p, &w_curs, offset, datalen);
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
reused++;
|
reused++;
|
||||||
@ -1016,9 +1031,11 @@ static void check_object(struct object_entry *entry)
|
|||||||
* We want in_pack_type even if we do not reuse delta
|
* We want in_pack_type even if we do not reuse delta
|
||||||
* since non-delta representations could still be reused.
|
* since non-delta representations could still be reused.
|
||||||
*/
|
*/
|
||||||
used = unpack_object_header_gently(buf, avail,
|
used = unpack_object_header_buffer(buf, avail,
|
||||||
&entry->in_pack_type,
|
&entry->in_pack_type,
|
||||||
&entry->size);
|
&entry->size);
|
||||||
|
if (used == 0)
|
||||||
|
goto give_up;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine if this is a delta and if so whether we can
|
* Determine if this is a delta and if so whether we can
|
||||||
@ -1030,6 +1047,8 @@ static void check_object(struct object_entry *entry)
|
|||||||
/* Not a delta hence we've already got all we need. */
|
/* Not a delta hence we've already got all we need. */
|
||||||
entry->type = entry->in_pack_type;
|
entry->type = entry->in_pack_type;
|
||||||
entry->in_pack_header_size = used;
|
entry->in_pack_header_size = used;
|
||||||
|
if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
|
||||||
|
goto give_up;
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
return;
|
return;
|
||||||
case OBJ_REF_DELTA:
|
case OBJ_REF_DELTA:
|
||||||
@ -1046,19 +1065,25 @@ static void check_object(struct object_entry *entry)
|
|||||||
ofs = c & 127;
|
ofs = c & 127;
|
||||||
while (c & 128) {
|
while (c & 128) {
|
||||||
ofs += 1;
|
ofs += 1;
|
||||||
if (!ofs || MSB(ofs, 7))
|
if (!ofs || MSB(ofs, 7)) {
|
||||||
die("delta base offset overflow in pack for %s",
|
error("delta base offset overflow in pack for %s",
|
||||||
sha1_to_hex(entry->idx.sha1));
|
sha1_to_hex(entry->idx.sha1));
|
||||||
|
goto give_up;
|
||||||
|
}
|
||||||
c = buf[used_0++];
|
c = buf[used_0++];
|
||||||
ofs = (ofs << 7) + (c & 127);
|
ofs = (ofs << 7) + (c & 127);
|
||||||
}
|
}
|
||||||
if (ofs >= entry->in_pack_offset)
|
|
||||||
die("delta base offset out of bound for %s",
|
|
||||||
sha1_to_hex(entry->idx.sha1));
|
|
||||||
ofs = entry->in_pack_offset - ofs;
|
ofs = entry->in_pack_offset - ofs;
|
||||||
|
if (ofs <= 0 || ofs >= entry->in_pack_offset) {
|
||||||
|
error("delta base offset out of bound for %s",
|
||||||
|
sha1_to_hex(entry->idx.sha1));
|
||||||
|
goto give_up;
|
||||||
|
}
|
||||||
if (reuse_delta && !entry->preferred_base) {
|
if (reuse_delta && !entry->preferred_base) {
|
||||||
struct revindex_entry *revidx;
|
struct revindex_entry *revidx;
|
||||||
revidx = find_pack_revindex(p, ofs);
|
revidx = find_pack_revindex(p, ofs);
|
||||||
|
if (!revidx)
|
||||||
|
goto give_up;
|
||||||
base_ref = nth_packed_object_sha1(p, revidx->nr);
|
base_ref = nth_packed_object_sha1(p, revidx->nr);
|
||||||
}
|
}
|
||||||
entry->in_pack_header_size = used + used_0;
|
entry->in_pack_header_size = used + used_0;
|
||||||
@ -1078,6 +1103,7 @@ static void check_object(struct object_entry *entry)
|
|||||||
*/
|
*/
|
||||||
entry->type = entry->in_pack_type;
|
entry->type = entry->in_pack_type;
|
||||||
entry->delta = base_entry;
|
entry->delta = base_entry;
|
||||||
|
entry->delta_size = entry->size;
|
||||||
entry->delta_sibling = base_entry->delta_child;
|
entry->delta_sibling = base_entry->delta_child;
|
||||||
base_entry->delta_child = entry;
|
base_entry->delta_child = entry;
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
@ -1092,6 +1118,8 @@ static void check_object(struct object_entry *entry)
|
|||||||
*/
|
*/
|
||||||
entry->size = get_size_from_delta(p, &w_curs,
|
entry->size = get_size_from_delta(p, &w_curs,
|
||||||
entry->in_pack_offset + entry->in_pack_header_size);
|
entry->in_pack_offset + entry->in_pack_header_size);
|
||||||
|
if (entry->size == 0)
|
||||||
|
goto give_up;
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1101,6 +1129,7 @@ static void check_object(struct object_entry *entry)
|
|||||||
* with sha1_object_info() to find about the object type
|
* with sha1_object_info() to find about the object type
|
||||||
* at this point...
|
* at this point...
|
||||||
*/
|
*/
|
||||||
|
give_up:
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1712,6 +1741,16 @@ static void prepare_pack(int window, int depth)
|
|||||||
|
|
||||||
get_object_details();
|
get_object_details();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're locally repacking then we need to be doubly careful
|
||||||
|
* from now on in order to make sure no stealth corruption gets
|
||||||
|
* propagated to the new pack. Clients receiving streamed packs
|
||||||
|
* should validate everything they get anyway so no need to incur
|
||||||
|
* the additional cost here in that case.
|
||||||
|
*/
|
||||||
|
if (!pack_to_stdout)
|
||||||
|
do_check_packed_object_crc = 1;
|
||||||
|
|
||||||
if (!nr_objects || !window || !depth)
|
if (!nr_objects || !window || !depth)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -370,6 +370,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
|||||||
base_offset = (base_offset << 7) + (c & 127);
|
base_offset = (base_offset << 7) + (c & 127);
|
||||||
}
|
}
|
||||||
base_offset = obj_list[nr].offset - base_offset;
|
base_offset = obj_list[nr].offset - base_offset;
|
||||||
|
if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
|
||||||
|
die("offset value out of bound for delta base object");
|
||||||
|
|
||||||
delta_data = get_data(delta_size);
|
delta_data = get_data(delta_size);
|
||||||
if (dry_run || !delta_data) {
|
if (dry_run || !delta_data) {
|
||||||
|
5
cache.h
5
cache.h
@ -574,6 +574,9 @@ extern int force_object_loose(const unsigned char *sha1, time_t mtime);
|
|||||||
/* just like read_sha1_file(), but non fatal in presence of bad objects */
|
/* just like read_sha1_file(), but non fatal in presence of bad objects */
|
||||||
extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
|
extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
|
||||||
|
|
||||||
|
/* global flag to enable extra checks when accessing packed objects */
|
||||||
|
extern int do_check_packed_object_crc;
|
||||||
|
|
||||||
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
|
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
|
||||||
|
|
||||||
extern int move_temp_to_file(const char *tmpfile, const char *filename);
|
extern int move_temp_to_file(const char *tmpfile, const char *filename);
|
||||||
@ -762,7 +765,7 @@ extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t
|
|||||||
extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
|
extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
|
||||||
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
|
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
|
||||||
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
|
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
|
||||||
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
||||||
extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
|
extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
|
||||||
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
|
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
|
||||||
extern int matches_pack_name(struct packed_git *p, const char *name);
|
extern int matches_pack_name(struct packed_git *p, const char *name);
|
||||||
|
@ -338,7 +338,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
|||||||
base_offset = (base_offset << 7) + (c & 127);
|
base_offset = (base_offset << 7) + (c & 127);
|
||||||
}
|
}
|
||||||
delta_base->offset = obj->idx.offset - base_offset;
|
delta_base->offset = obj->idx.offset - base_offset;
|
||||||
if (delta_base->offset >= obj->idx.offset)
|
if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
|
||||||
bad_object(obj->idx.offset, "delta base offset is out of bound");
|
bad_object(obj->idx.offset, "delta base offset is out of bound");
|
||||||
break;
|
break;
|
||||||
case OBJ_COMMIT:
|
case OBJ_COMMIT:
|
||||||
|
@ -140,7 +140,8 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
|
|||||||
else
|
else
|
||||||
lo = mi + 1;
|
lo = mi + 1;
|
||||||
} while (lo < hi);
|
} while (lo < hi);
|
||||||
die("internal error: pack revindex corrupt");
|
error("bad offset for revindex");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void discard_revindex(void)
|
void discard_revindex(void)
|
||||||
|
83
sha1_file.c
83
sha1_file.c
@ -1122,7 +1122,8 @@ static int legacy_loose_object(unsigned char *map)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
|
unsigned long unpack_object_header_buffer(const unsigned char *buf,
|
||||||
|
unsigned long len, enum object_type *type, unsigned long *sizep)
|
||||||
{
|
{
|
||||||
unsigned shift;
|
unsigned shift;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
@ -1134,10 +1135,10 @@ unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned lon
|
|||||||
size = c & 15;
|
size = c & 15;
|
||||||
shift = 4;
|
shift = 4;
|
||||||
while (c & 0x80) {
|
while (c & 0x80) {
|
||||||
if (len <= used)
|
if (len <= used || sizeof(long) * 8 <= shift) {
|
||||||
return 0;
|
error("bad object header");
|
||||||
if (sizeof(long) * 8 <= shift)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
c = buf[used++];
|
c = buf[used++];
|
||||||
size += (c & 0x7f) << shift;
|
size += (c & 0x7f) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
@ -1176,7 +1177,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
|
|||||||
* really worth it and we don't write it any longer. But we
|
* really worth it and we don't write it any longer. But we
|
||||||
* can still read it.
|
* can still read it.
|
||||||
*/
|
*/
|
||||||
used = unpack_object_header_gently(map, mapsize, &type, &size);
|
used = unpack_object_header_buffer(map, mapsize, &type, &size);
|
||||||
if (!used || !valid_loose_object_type[type])
|
if (!used || !valid_loose_object_type[type])
|
||||||
return -1;
|
return -1;
|
||||||
map += used;
|
map += used;
|
||||||
@ -1325,8 +1326,10 @@ unsigned long get_size_from_delta(struct packed_git *p,
|
|||||||
} while ((st == Z_OK || st == Z_BUF_ERROR) &&
|
} while ((st == Z_OK || st == Z_BUF_ERROR) &&
|
||||||
stream.total_out < sizeof(delta_head));
|
stream.total_out < sizeof(delta_head));
|
||||||
inflateEnd(&stream);
|
inflateEnd(&stream);
|
||||||
if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
|
if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
|
||||||
die("delta data unpack-initial failed");
|
error("delta data unpack-initial failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Examine the initial part of the delta to figure out
|
/* Examine the initial part of the delta to figure out
|
||||||
* the result size.
|
* the result size.
|
||||||
@ -1367,7 +1370,7 @@ static off_t get_delta_base(struct packed_git *p,
|
|||||||
base_offset = (base_offset << 7) + (c & 127);
|
base_offset = (base_offset << 7) + (c & 127);
|
||||||
}
|
}
|
||||||
base_offset = delta_obj_offset - base_offset;
|
base_offset = delta_obj_offset - base_offset;
|
||||||
if (base_offset >= delta_obj_offset)
|
if (base_offset <= 0 || base_offset >= delta_obj_offset)
|
||||||
return 0; /* out of bound */
|
return 0; /* out of bound */
|
||||||
*curpos += used;
|
*curpos += used;
|
||||||
} else if (type == OBJ_REF_DELTA) {
|
} else if (type == OBJ_REF_DELTA) {
|
||||||
@ -1393,15 +1396,32 @@ static int packed_delta_info(struct packed_git *p,
|
|||||||
off_t base_offset;
|
off_t base_offset;
|
||||||
|
|
||||||
base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
|
base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
|
||||||
|
if (!base_offset)
|
||||||
|
return OBJ_BAD;
|
||||||
type = packed_object_info(p, base_offset, NULL);
|
type = packed_object_info(p, base_offset, NULL);
|
||||||
|
if (type <= OBJ_NONE) {
|
||||||
|
struct revindex_entry *revidx;
|
||||||
|
const unsigned char *base_sha1;
|
||||||
|
revidx = find_pack_revindex(p, base_offset);
|
||||||
|
if (!revidx)
|
||||||
|
return OBJ_BAD;
|
||||||
|
base_sha1 = nth_packed_object_sha1(p, revidx->nr);
|
||||||
|
mark_bad_packed_object(p, base_sha1);
|
||||||
|
type = sha1_object_info(base_sha1, NULL);
|
||||||
|
if (type <= OBJ_NONE)
|
||||||
|
return OBJ_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
/* We choose to only get the type of the base object and
|
/* We choose to only get the type of the base object and
|
||||||
* ignore potentially corrupt pack file that expects the delta
|
* ignore potentially corrupt pack file that expects the delta
|
||||||
* based on a base with a wrong size. This saves tons of
|
* based on a base with a wrong size. This saves tons of
|
||||||
* inflate() calls.
|
* inflate() calls.
|
||||||
*/
|
*/
|
||||||
if (sizep)
|
if (sizep) {
|
||||||
*sizep = get_size_from_delta(p, w_curs, curpos);
|
*sizep = get_size_from_delta(p, w_curs, curpos);
|
||||||
|
if (*sizep == 0)
|
||||||
|
type = OBJ_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -1423,9 +1443,10 @@ static int unpack_object_header(struct packed_git *p,
|
|||||||
* insane, so we know won't exceed what we have been given.
|
* insane, so we know won't exceed what we have been given.
|
||||||
*/
|
*/
|
||||||
base = use_pack(p, w_curs, *curpos, &left);
|
base = use_pack(p, w_curs, *curpos, &left);
|
||||||
used = unpack_object_header_gently(base, left, &type, sizep);
|
used = unpack_object_header_buffer(base, left, &type, sizep);
|
||||||
if (!used)
|
if (!used) {
|
||||||
die("object offset outside of pack file");
|
type = OBJ_BAD;
|
||||||
|
} else
|
||||||
*curpos += used;
|
*curpos += used;
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
@ -1510,8 +1531,9 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
|
|||||||
*sizep = size;
|
*sizep = size;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
die("pack %s contains unknown object type %d",
|
error("unknown object type %i at offset %"PRIuMAX" in %s",
|
||||||
p->pack_name, type);
|
type, (uintmax_t)obj_offset, p->pack_name);
|
||||||
|
type = OBJ_BAD;
|
||||||
}
|
}
|
||||||
unuse_pack(&w_curs);
|
unuse_pack(&w_curs);
|
||||||
return type;
|
return type;
|
||||||
@ -1675,9 +1697,12 @@ static void *unpack_delta_entry(struct packed_git *p,
|
|||||||
* This is costly but should happen only in the presence
|
* This is costly but should happen only in the presence
|
||||||
* of a corrupted pack, and is better than failing outright.
|
* of a corrupted pack, and is better than failing outright.
|
||||||
*/
|
*/
|
||||||
struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
|
struct revindex_entry *revidx;
|
||||||
const unsigned char *base_sha1 =
|
const unsigned char *base_sha1;
|
||||||
nth_packed_object_sha1(p, revidx->nr);
|
revidx = find_pack_revindex(p, base_offset);
|
||||||
|
if (!revidx)
|
||||||
|
return NULL;
|
||||||
|
base_sha1 = nth_packed_object_sha1(p, revidx->nr);
|
||||||
error("failed to read delta base object %s"
|
error("failed to read delta base object %s"
|
||||||
" at offset %"PRIuMAX" from %s",
|
" at offset %"PRIuMAX" from %s",
|
||||||
sha1_to_hex(base_sha1), (uintmax_t)base_offset,
|
sha1_to_hex(base_sha1), (uintmax_t)base_offset,
|
||||||
@ -1706,6 +1731,8 @@ static void *unpack_delta_entry(struct packed_git *p,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_check_packed_object_crc;
|
||||||
|
|
||||||
void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||||
enum object_type *type, unsigned long *sizep)
|
enum object_type *type, unsigned long *sizep)
|
||||||
{
|
{
|
||||||
@ -1713,6 +1740,19 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
|||||||
off_t curpos = obj_offset;
|
off_t curpos = obj_offset;
|
||||||
void *data;
|
void *data;
|
||||||
|
|
||||||
|
if (do_check_packed_object_crc && p->index_version > 1) {
|
||||||
|
struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
|
||||||
|
unsigned long len = revidx[1].offset - obj_offset;
|
||||||
|
if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
|
||||||
|
const unsigned char *sha1 =
|
||||||
|
nth_packed_object_sha1(p, revidx->nr);
|
||||||
|
error("bad packed object CRC for %s",
|
||||||
|
sha1_to_hex(sha1));
|
||||||
|
mark_bad_packed_object(p, sha1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*type = unpack_object_header(p, &w_curs, &curpos, sizep);
|
*type = unpack_object_header(p, &w_curs, &curpos, sizep);
|
||||||
switch (*type) {
|
switch (*type) {
|
||||||
case OBJ_OFS_DELTA:
|
case OBJ_OFS_DELTA:
|
||||||
@ -1966,7 +2006,14 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
|
|||||||
if (!find_pack_entry(sha1, &e, NULL))
|
if (!find_pack_entry(sha1, &e, NULL))
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
return packed_object_info(e.p, e.offset, sizep);
|
|
||||||
|
status = packed_object_info(e.p, e.offset, sizep);
|
||||||
|
if (status < 0) {
|
||||||
|
mark_bad_packed_object(e.p, sha1);
|
||||||
|
status = sha1_object_info(sha1, sizep);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *read_packed_sha1(const unsigned char *sha1,
|
static void *read_packed_sha1(const unsigned char *sha1,
|
||||||
|
@ -196,7 +196,8 @@ test_expect_success \
|
|||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'[index v2] 5) pack-objects refuses to reuse corrupted data' \
|
'[index v2] 5) pack-objects refuses to reuse corrupted data' \
|
||||||
'test_must_fail git pack-objects test-5 <obj-list'
|
'test_must_fail git pack-objects test-5 <obj-list &&
|
||||||
|
test_must_fail git pack-objects --no-reuse-object test-6 <obj-list'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'[index v2] 6) verify-pack detects CRC mismatch' \
|
'[index v2] 6) verify-pack detects CRC mismatch' \
|
||||||
|
@ -41,11 +41,17 @@ create_new_pack() {
|
|||||||
git verify-pack -v ${pack}.pack
|
git verify-pack -v ${pack}.pack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_repack() {
|
||||||
|
pack=`printf "$blob_1\n$blob_2\n$blob_3\n" |
|
||||||
|
git pack-objects $@ .git/objects/pack/pack` &&
|
||||||
|
pack=".git/objects/pack/pack-${pack}"
|
||||||
|
}
|
||||||
|
|
||||||
do_corrupt_object() {
|
do_corrupt_object() {
|
||||||
ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
|
ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
|
||||||
ofs=$(($ofs + $2)) &&
|
ofs=$(($ofs + $2)) &&
|
||||||
chmod +w ${pack}.pack &&
|
chmod +w ${pack}.pack &&
|
||||||
dd if=/dev/zero of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
|
dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
|
||||||
test_must_fail git verify-pack ${pack}.pack
|
test_must_fail git verify-pack ${pack}.pack
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +66,7 @@ test_expect_success \
|
|||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'create corruption in header of first object' \
|
'create corruption in header of first object' \
|
||||||
'do_corrupt_object $blob_1 0 &&
|
'do_corrupt_object $blob_1 0 < /dev/zero &&
|
||||||
test_must_fail git cat-file blob $blob_1 > /dev/null &&
|
test_must_fail git cat-file blob $blob_1 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
||||||
@ -119,7 +125,7 @@ test_expect_success \
|
|||||||
'create corruption in header of first delta' \
|
'create corruption in header of first delta' \
|
||||||
'create_new_pack &&
|
'create_new_pack &&
|
||||||
git prune-packed &&
|
git prune-packed &&
|
||||||
do_corrupt_object $blob_2 0 &&
|
do_corrupt_object $blob_2 0 < /dev/zero &&
|
||||||
git cat-file blob $blob_1 > /dev/null &&
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
||||||
@ -133,6 +139,15 @@ test_expect_success \
|
|||||||
git cat-file blob $blob_2 > /dev/null &&
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
git cat-file blob $blob_3 > /dev/null'
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... and then a repack "clears" the corruption' \
|
||||||
|
'do_repack &&
|
||||||
|
git prune-packed &&
|
||||||
|
git verify-pack ${pack}.pack &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'create corruption in data of first delta' \
|
'create corruption in data of first delta' \
|
||||||
'create_new_pack &&
|
'create_new_pack &&
|
||||||
@ -152,11 +167,20 @@ test_expect_success \
|
|||||||
git cat-file blob $blob_2 > /dev/null &&
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
git cat-file blob $blob_3 > /dev/null'
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... and then a repack "clears" the corruption' \
|
||||||
|
'do_repack &&
|
||||||
|
git prune-packed &&
|
||||||
|
git verify-pack ${pack}.pack &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
|
'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
|
||||||
'create_new_pack &&
|
'create_new_pack &&
|
||||||
git prune-packed &&
|
git prune-packed &&
|
||||||
do_corrupt_object $blob_2 2 &&
|
do_corrupt_object $blob_2 2 < /dev/zero &&
|
||||||
git cat-file blob $blob_1 > /dev/null &&
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
||||||
@ -171,17 +195,75 @@ test_expect_success \
|
|||||||
git cat-file blob $blob_3 > /dev/null'
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'corruption in delta base reference of first delta (OBJ_OFS_DELTA)' \
|
'... and then a repack "clears" the corruption' \
|
||||||
|
'do_repack &&
|
||||||
|
git prune-packed &&
|
||||||
|
git verify-pack ${pack}.pack &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
|
||||||
'create_new_pack --delta-base-offset &&
|
'create_new_pack --delta-base-offset &&
|
||||||
git prune-packed &&
|
git prune-packed &&
|
||||||
do_corrupt_object $blob_2 2 &&
|
do_corrupt_object $blob_2 2 < /dev/zero &&
|
||||||
git cat-file blob $blob_1 > /dev/null &&
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
test_expect_success \
|
test_expect_success \
|
||||||
'... and a redundant pack allows for full recovery too' \
|
'... but having a loose copy allows for full recovery' \
|
||||||
'mv ${pack}.idx tmp &&
|
'mv ${pack}.idx tmp &&
|
||||||
|
git hash-object -t blob -w file_2 &&
|
||||||
|
mv tmp ${pack}.idx &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... and then a repack "clears" the corruption' \
|
||||||
|
'do_repack --delta-base-offset &&
|
||||||
|
git prune-packed &&
|
||||||
|
git verify-pack ${pack}.pack &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
|
||||||
|
'create_new_pack --delta-base-offset &&
|
||||||
|
git prune-packed &&
|
||||||
|
printf "\001" | do_corrupt_object $blob_2 2 &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
test_must_fail git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... but having a loose copy allows for full recovery' \
|
||||||
|
'mv ${pack}.idx tmp &&
|
||||||
|
git hash-object -t blob -w file_2 &&
|
||||||
|
mv tmp ${pack}.idx &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... and then a repack "clears" the corruption' \
|
||||||
|
'do_repack --delta-base-offset &&
|
||||||
|
git prune-packed &&
|
||||||
|
git verify-pack ${pack}.pack &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
git cat-file blob $blob_3 > /dev/null'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'... and a redundant pack allows for full recovery too' \
|
||||||
|
'do_corrupt_object $blob_2 2 < /dev/zero &&
|
||||||
|
git cat-file blob $blob_1 > /dev/null &&
|
||||||
|
test_must_fail git cat-file blob $blob_2 > /dev/null &&
|
||||||
|
test_must_fail git cat-file blob $blob_3 > /dev/null &&
|
||||||
|
mv ${pack}.idx tmp &&
|
||||||
git hash-object -t blob -w file_1 &&
|
git hash-object -t blob -w file_1 &&
|
||||||
git hash-object -t blob -w file_2 &&
|
git hash-object -t blob -w file_2 &&
|
||||||
printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
|
printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
|
||||||
|
Loading…
Reference in New Issue
Block a user