Merge branch 'ab/fsck-unexpected-type'
"git fsck" has been taught to report mismatch between expected and actual types of an object better. * ab/fsck-unexpected-type: fsck: report invalid object type-path combinations fsck: don't hard die on invalid object types object-file.c: stop dying in parse_loose_header() object-file.c: return ULHR_TOO_LONG on "header too long" object-file.c: use "enum" return type for unpack_loose_header() object-file.c: simplify unpack_loose_short_header() object-file.c: make parse_loose_header_extended() public object-file.c: return -1, not "status" from unpack_loose_header() object-file.c: don't set "typep" when returning non-zero cat-file tests: test for current --allow-unknown-type behavior cat-file tests: add corrupt loose object test cat-file tests: test for missing/bogus object with -t, -s and -p cat-file tests: move bogus_* variable declarations earlier fsck tests: test for garbage appended to a loose object fsck tests: test current hash/type mismatch behavior fsck tests: refactor one test to use a sub-repo fsck tests: add test for fsck-ing an unknown type
This commit is contained in:
commit
061a21d36d
@ -312,7 +312,7 @@ static void export_blob(const struct object_id *oid)
|
|||||||
if (!buf)
|
if (!buf)
|
||||||
die("could not read blob %s", oid_to_hex(oid));
|
die("could not read blob %s", oid_to_hex(oid));
|
||||||
if (check_object_signature(the_repository, oid, buf, size,
|
if (check_object_signature(the_repository, oid, buf, size,
|
||||||
type_name(type)) < 0)
|
type_name(type), NULL) < 0)
|
||||||
die("oid mismatch in blob %s", oid_to_hex(oid));
|
die("oid mismatch in blob %s", oid_to_hex(oid));
|
||||||
object = parse_object_buffer(the_repository, oid, type,
|
object = parse_object_buffer(the_repository, oid, type,
|
||||||
size, buf, &eaten);
|
size, buf, &eaten);
|
||||||
|
@ -593,18 +593,43 @@ static void get_default_heads(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct for_each_loose_cb
|
||||||
|
{
|
||||||
|
struct progress *progress;
|
||||||
|
struct strbuf obj_type;
|
||||||
|
};
|
||||||
|
|
||||||
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
|
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
|
||||||
{
|
{
|
||||||
|
struct for_each_loose_cb *cb_data = data;
|
||||||
struct object *obj;
|
struct object *obj;
|
||||||
enum object_type type;
|
enum object_type type = OBJ_NONE;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
void *contents;
|
void *contents;
|
||||||
int eaten;
|
int eaten;
|
||||||
|
struct object_info oi = OBJECT_INFO_INIT;
|
||||||
|
struct object_id real_oid = *null_oid();
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
|
strbuf_reset(&cb_data->obj_type);
|
||||||
|
oi.type_name = &cb_data->obj_type;
|
||||||
|
oi.sizep = &size;
|
||||||
|
oi.typep = &type;
|
||||||
|
|
||||||
|
if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) {
|
||||||
|
if (contents && !oideq(&real_oid, oid))
|
||||||
|
err = error(_("%s: hash-path mismatch, found at: %s"),
|
||||||
|
oid_to_hex(&real_oid), path);
|
||||||
|
else
|
||||||
|
err = error(_("%s: object corrupt or missing: %s"),
|
||||||
|
oid_to_hex(oid), path);
|
||||||
|
}
|
||||||
|
if (type != OBJ_NONE && type < 0)
|
||||||
|
err = error(_("%s: object is of unknown type '%s': %s"),
|
||||||
|
oid_to_hex(&real_oid), cb_data->obj_type.buf,
|
||||||
|
path);
|
||||||
|
if (err < 0) {
|
||||||
errors_found |= ERROR_OBJECT;
|
errors_found |= ERROR_OBJECT;
|
||||||
error(_("%s: object corrupt or missing: %s"),
|
|
||||||
oid_to_hex(oid), path);
|
|
||||||
return 0; /* keep checking other objects */
|
return 0; /* keep checking other objects */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,8 +665,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsck_subdir(unsigned int nr, const char *path, void *progress)
|
static int fsck_subdir(unsigned int nr, const char *path, void *data)
|
||||||
{
|
{
|
||||||
|
struct for_each_loose_cb *cb_data = data;
|
||||||
|
struct progress *progress = cb_data->progress;
|
||||||
display_progress(progress, nr + 1);
|
display_progress(progress, nr + 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -649,6 +676,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress)
|
|||||||
static void fsck_object_dir(const char *path)
|
static void fsck_object_dir(const char *path)
|
||||||
{
|
{
|
||||||
struct progress *progress = NULL;
|
struct progress *progress = NULL;
|
||||||
|
struct for_each_loose_cb cb_data = {
|
||||||
|
.obj_type = STRBUF_INIT,
|
||||||
|
.progress = progress,
|
||||||
|
};
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf_ln(stderr, _("Checking object directory"));
|
fprintf_ln(stderr, _("Checking object directory"));
|
||||||
@ -657,9 +688,10 @@ static void fsck_object_dir(const char *path)
|
|||||||
progress = start_progress(_("Checking object directories"), 256);
|
progress = start_progress(_("Checking object directories"), 256);
|
||||||
|
|
||||||
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
|
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
|
||||||
progress);
|
&cb_data);
|
||||||
display_progress(progress, 256);
|
display_progress(progress, 256);
|
||||||
stop_progress(&progress);
|
stop_progress(&progress);
|
||||||
|
strbuf_release(&cb_data.obj_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsck_head_link(const char *head_ref_name,
|
static int fsck_head_link(const char *head_ref_name,
|
||||||
|
@ -1415,7 +1415,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
|
|||||||
|
|
||||||
if (check_object_signature(the_repository, &d->oid,
|
if (check_object_signature(the_repository, &d->oid,
|
||||||
data, size,
|
data, size,
|
||||||
type_name(type)))
|
type_name(type), NULL))
|
||||||
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
|
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
|
|||||||
|
|
||||||
repl = lookup_replace_object(the_repository, tagged_oid);
|
repl = lookup_replace_object(the_repository, tagged_oid);
|
||||||
ret = check_object_signature(the_repository, repl,
|
ret = check_object_signature(the_repository, repl,
|
||||||
buffer, size, type_name(*tagged_type));
|
buffer, size, type_name(*tagged_type),
|
||||||
|
NULL);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
45
cache.h
45
cache.h
@ -1268,11 +1268,50 @@ char *xdg_cache_home(const char *filename);
|
|||||||
|
|
||||||
int git_open_cloexec(const char *name, int flags);
|
int git_open_cloexec(const char *name, int flags);
|
||||||
#define git_open(name) git_open_cloexec(name, O_RDONLY)
|
#define git_open(name) git_open_cloexec(name, O_RDONLY)
|
||||||
int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
|
|
||||||
int parse_loose_header(const char *hdr, unsigned long *sizep);
|
/**
|
||||||
|
* unpack_loose_header() initializes the data stream needed to unpack
|
||||||
|
* a loose object header.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* - ULHR_OK on success
|
||||||
|
* - ULHR_BAD on error
|
||||||
|
* - ULHR_TOO_LONG if the header was too long
|
||||||
|
*
|
||||||
|
* It will only parse up to MAX_HEADER_LEN bytes unless an optional
|
||||||
|
* "hdrbuf" argument is non-NULL. This is intended for use with
|
||||||
|
* OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
|
||||||
|
* reporting. The full header will be extracted to "hdrbuf" for use
|
||||||
|
* with parse_loose_header(), ULHR_TOO_LONG will still be returned
|
||||||
|
* from this function to indicate that the header was too long.
|
||||||
|
*/
|
||||||
|
enum unpack_loose_header_result {
|
||||||
|
ULHR_OK,
|
||||||
|
ULHR_BAD,
|
||||||
|
ULHR_TOO_LONG,
|
||||||
|
};
|
||||||
|
enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
|
||||||
|
unsigned char *map,
|
||||||
|
unsigned long mapsize,
|
||||||
|
void *buffer,
|
||||||
|
unsigned long bufsiz,
|
||||||
|
struct strbuf *hdrbuf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse_loose_header() parses the starting "<type> <len>\0" of an
|
||||||
|
* object. If it doesn't follow that format -1 is returned. To check
|
||||||
|
* the validity of the <type> populate the "typep" in the "struct
|
||||||
|
* object_info". It will be OBJ_BAD if the object type is unknown. The
|
||||||
|
* parsed <len> can be retrieved via "oi->sizep", and from there
|
||||||
|
* passed to unpack_loose_rest().
|
||||||
|
*/
|
||||||
|
struct object_info;
|
||||||
|
int parse_loose_header(const char *hdr, struct object_info *oi);
|
||||||
|
|
||||||
int check_object_signature(struct repository *r, const struct object_id *oid,
|
int check_object_signature(struct repository *r, const struct object_id *oid,
|
||||||
void *buf, unsigned long size, const char *type);
|
void *buf, unsigned long size, const char *type,
|
||||||
|
struct object_id *real_oidp);
|
||||||
|
|
||||||
int finalize_object_file(const char *tmpfile, const char *filename);
|
int finalize_object_file(const char *tmpfile, const char *filename);
|
||||||
|
|
||||||
|
176
object-file.c
176
object-file.c
@ -1016,9 +1016,11 @@ void *xmmap(void *start, size_t length,
|
|||||||
* the streaming interface and rehash it to do the same.
|
* the streaming interface and rehash it to do the same.
|
||||||
*/
|
*/
|
||||||
int check_object_signature(struct repository *r, const struct object_id *oid,
|
int check_object_signature(struct repository *r, const struct object_id *oid,
|
||||||
void *map, unsigned long size, const char *type)
|
void *map, unsigned long size, const char *type,
|
||||||
|
struct object_id *real_oidp)
|
||||||
{
|
{
|
||||||
struct object_id real_oid;
|
struct object_id tmp;
|
||||||
|
struct object_id *real_oid = real_oidp ? real_oidp : &tmp;
|
||||||
enum object_type obj_type;
|
enum object_type obj_type;
|
||||||
struct git_istream *st;
|
struct git_istream *st;
|
||||||
git_hash_ctx c;
|
git_hash_ctx c;
|
||||||
@ -1026,8 +1028,8 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
|
|||||||
int hdrlen;
|
int hdrlen;
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
hash_object_file(r->hash_algo, map, size, type, &real_oid);
|
hash_object_file(r->hash_algo, map, size, type, real_oid);
|
||||||
return !oideq(oid, &real_oid) ? -1 : 0;
|
return !oideq(oid, real_oid) ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
st = open_istream(r, oid, &obj_type, &size, NULL);
|
st = open_istream(r, oid, &obj_type, &size, NULL);
|
||||||
@ -1052,9 +1054,9 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
|
|||||||
break;
|
break;
|
||||||
r->hash_algo->update_fn(&c, buf, readlen);
|
r->hash_algo->update_fn(&c, buf, readlen);
|
||||||
}
|
}
|
||||||
r->hash_algo->final_oid_fn(&real_oid, &c);
|
r->hash_algo->final_oid_fn(real_oid, &c);
|
||||||
close_istream(st);
|
close_istream(st);
|
||||||
return !oideq(oid, &real_oid) ? -1 : 0;
|
return !oideq(oid, real_oid) ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_open_cloexec(const char *name, int flags)
|
int git_open_cloexec(const char *name, int flags)
|
||||||
@ -1187,11 +1189,14 @@ void *map_loose_object(struct repository *r,
|
|||||||
return map_loose_object_1(r, NULL, oid, size);
|
return map_loose_object_1(r, NULL, oid, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unpack_loose_short_header(git_zstream *stream,
|
enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
|
||||||
unsigned char *map, unsigned long mapsize,
|
unsigned char *map,
|
||||||
void *buffer, unsigned long bufsiz)
|
unsigned long mapsize,
|
||||||
|
void *buffer,
|
||||||
|
unsigned long bufsiz,
|
||||||
|
struct strbuf *header)
|
||||||
{
|
{
|
||||||
int ret;
|
int status;
|
||||||
|
|
||||||
/* Get the data stream */
|
/* Get the data stream */
|
||||||
memset(stream, 0, sizeof(*stream));
|
memset(stream, 0, sizeof(*stream));
|
||||||
@ -1202,43 +1207,24 @@ static int unpack_loose_short_header(git_zstream *stream,
|
|||||||
|
|
||||||
git_inflate_init(stream);
|
git_inflate_init(stream);
|
||||||
obj_read_unlock();
|
obj_read_unlock();
|
||||||
ret = git_inflate(stream, 0);
|
status = git_inflate(stream, 0);
|
||||||
obj_read_lock();
|
obj_read_lock();
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int unpack_loose_header(git_zstream *stream,
|
|
||||||
unsigned char *map, unsigned long mapsize,
|
|
||||||
void *buffer, unsigned long bufsiz)
|
|
||||||
{
|
|
||||||
int status = unpack_loose_short_header(stream, map, mapsize,
|
|
||||||
buffer, bufsiz);
|
|
||||||
|
|
||||||
if (status < Z_OK)
|
if (status < Z_OK)
|
||||||
return status;
|
return ULHR_BAD;
|
||||||
|
|
||||||
/* Make sure we have the terminating NUL */
|
|
||||||
if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
|
|
||||||
return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map,
|
|
||||||
unsigned long mapsize, void *buffer,
|
|
||||||
unsigned long bufsiz, struct strbuf *header)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
status = unpack_loose_short_header(stream, map, mapsize, buffer, bufsiz);
|
|
||||||
if (status < Z_OK)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if entire header is unpacked in the first iteration.
|
* Check if entire header is unpacked in the first iteration.
|
||||||
*/
|
*/
|
||||||
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
|
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
|
||||||
return 0;
|
return ULHR_OK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have a header longer than MAX_HEADER_LEN. The "header"
|
||||||
|
* here is only non-NULL when we run "cat-file
|
||||||
|
* --allow-unknown-type".
|
||||||
|
*/
|
||||||
|
if (!header)
|
||||||
|
return ULHR_TOO_LONG;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* buffer[0..bufsiz] was not large enough. Copy the partial
|
* buffer[0..bufsiz] was not large enough. Copy the partial
|
||||||
@ -1259,7 +1245,7 @@ static int unpack_loose_header_to_strbuf(git_zstream *stream, unsigned char *map
|
|||||||
stream->next_out = buffer;
|
stream->next_out = buffer;
|
||||||
stream->avail_out = bufsiz;
|
stream->avail_out = bufsiz;
|
||||||
} while (status != Z_STREAM_END);
|
} while (status != Z_STREAM_END);
|
||||||
return -1;
|
return ULHR_TOO_LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *unpack_loose_rest(git_zstream *stream,
|
static void *unpack_loose_rest(git_zstream *stream,
|
||||||
@ -1317,8 +1303,7 @@ static void *unpack_loose_rest(git_zstream *stream,
|
|||||||
* too permissive for what we want to check. So do an anal
|
* too permissive for what we want to check. So do an anal
|
||||||
* object header parse by hand.
|
* object header parse by hand.
|
||||||
*/
|
*/
|
||||||
static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
|
int parse_loose_header(const char *hdr, struct object_info *oi)
|
||||||
unsigned int flags)
|
|
||||||
{
|
{
|
||||||
const char *type_buf = hdr;
|
const char *type_buf = hdr;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
@ -1340,15 +1325,6 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
|
|||||||
type = type_from_string_gently(type_buf, type_len, 1);
|
type = type_from_string_gently(type_buf, type_len, 1);
|
||||||
if (oi->type_name)
|
if (oi->type_name)
|
||||||
strbuf_add(oi->type_name, type_buf, type_len);
|
strbuf_add(oi->type_name, type_buf, type_len);
|
||||||
/*
|
|
||||||
* Set type to 0 if its an unknown object and
|
|
||||||
* we're obtaining the type using '--allow-unknown-type'
|
|
||||||
* option.
|
|
||||||
*/
|
|
||||||
if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0))
|
|
||||||
type = 0;
|
|
||||||
else if (type < 0)
|
|
||||||
die(_("invalid object type"));
|
|
||||||
if (oi->typep)
|
if (oi->typep)
|
||||||
*oi->typep = type;
|
*oi->typep = type;
|
||||||
|
|
||||||
@ -1375,15 +1351,14 @@ static int parse_loose_header_extended(const char *hdr, struct object_info *oi,
|
|||||||
/*
|
/*
|
||||||
* The length must be followed by a zero byte
|
* The length must be followed by a zero byte
|
||||||
*/
|
*/
|
||||||
return *hdr ? -1 : type;
|
if (*hdr)
|
||||||
}
|
return -1;
|
||||||
|
|
||||||
int parse_loose_header(const char *hdr, unsigned long *sizep)
|
/*
|
||||||
{
|
* The format is valid, but the type may still be bogus. The
|
||||||
struct object_info oi = OBJECT_INFO_INIT;
|
* Caller needs to check its oi->typep.
|
||||||
|
*/
|
||||||
oi.sizep = sizep;
|
return 0;
|
||||||
return parse_loose_header_extended(hdr, &oi, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int loose_object_info(struct repository *r,
|
static int loose_object_info(struct repository *r,
|
||||||
@ -1397,6 +1372,8 @@ static int loose_object_info(struct repository *r,
|
|||||||
char hdr[MAX_HEADER_LEN];
|
char hdr[MAX_HEADER_LEN];
|
||||||
struct strbuf hdrbuf = STRBUF_INIT;
|
struct strbuf hdrbuf = STRBUF_INIT;
|
||||||
unsigned long size_scratch;
|
unsigned long size_scratch;
|
||||||
|
enum object_type type_scratch;
|
||||||
|
int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;
|
||||||
|
|
||||||
if (oi->delta_base_oid)
|
if (oi->delta_base_oid)
|
||||||
oidclr(oi->delta_base_oid);
|
oidclr(oi->delta_base_oid);
|
||||||
@ -1427,43 +1404,48 @@ static int loose_object_info(struct repository *r,
|
|||||||
|
|
||||||
if (!oi->sizep)
|
if (!oi->sizep)
|
||||||
oi->sizep = &size_scratch;
|
oi->sizep = &size_scratch;
|
||||||
|
if (!oi->typep)
|
||||||
|
oi->typep = &type_scratch;
|
||||||
|
|
||||||
if (oi->disk_sizep)
|
if (oi->disk_sizep)
|
||||||
*oi->disk_sizep = mapsize;
|
*oi->disk_sizep = mapsize;
|
||||||
if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) {
|
|
||||||
if (unpack_loose_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
|
switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
|
||||||
status = error(_("unable to unpack %s header with --allow-unknown-type"),
|
allow_unknown ? &hdrbuf : NULL)) {
|
||||||
oid_to_hex(oid));
|
case ULHR_OK:
|
||||||
} else if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
|
if (parse_loose_header(hdrbuf.len ? hdrbuf.buf : hdr, oi) < 0)
|
||||||
|
status = error(_("unable to parse %s header"), oid_to_hex(oid));
|
||||||
|
else if (!allow_unknown && *oi->typep < 0)
|
||||||
|
die(_("invalid object type"));
|
||||||
|
|
||||||
|
if (!oi->contentp)
|
||||||
|
break;
|
||||||
|
*oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
|
||||||
|
if (*oi->contentp)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
status = -1;
|
||||||
|
break;
|
||||||
|
case ULHR_BAD:
|
||||||
status = error(_("unable to unpack %s header"),
|
status = error(_("unable to unpack %s header"),
|
||||||
oid_to_hex(oid));
|
oid_to_hex(oid));
|
||||||
if (status < 0)
|
break;
|
||||||
; /* Do nothing */
|
case ULHR_TOO_LONG:
|
||||||
else if (hdrbuf.len) {
|
status = error(_("header for %s too long, exceeds %d bytes"),
|
||||||
if ((status = parse_loose_header_extended(hdrbuf.buf, oi, flags)) < 0)
|
oid_to_hex(oid), MAX_HEADER_LEN);
|
||||||
status = error(_("unable to parse %s header with --allow-unknown-type"),
|
break;
|
||||||
oid_to_hex(oid));
|
}
|
||||||
} else if ((status = parse_loose_header_extended(hdr, oi, flags)) < 0)
|
|
||||||
status = error(_("unable to parse %s header"), oid_to_hex(oid));
|
|
||||||
|
|
||||||
if (status >= 0 && oi->contentp) {
|
|
||||||
*oi->contentp = unpack_loose_rest(&stream, hdr,
|
|
||||||
*oi->sizep, oid);
|
|
||||||
if (!*oi->contentp) {
|
|
||||||
git_inflate_end(&stream);
|
|
||||||
status = -1;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
git_inflate_end(&stream);
|
|
||||||
|
|
||||||
|
git_inflate_end(&stream);
|
||||||
|
cleanup:
|
||||||
munmap(map, mapsize);
|
munmap(map, mapsize);
|
||||||
if (status && oi->typep)
|
|
||||||
*oi->typep = status;
|
|
||||||
if (oi->sizep == &size_scratch)
|
if (oi->sizep == &size_scratch)
|
||||||
oi->sizep = NULL;
|
oi->sizep = NULL;
|
||||||
strbuf_release(&hdrbuf);
|
strbuf_release(&hdrbuf);
|
||||||
|
if (oi->typep == &type_scratch)
|
||||||
|
oi->typep = NULL;
|
||||||
oi->whence = OI_LOOSE;
|
oi->whence = OI_LOOSE;
|
||||||
return (status < 0) ? status : 0;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int obj_read_use_lock = 0;
|
int obj_read_use_lock = 0;
|
||||||
@ -2524,17 +2506,16 @@ static int check_stream_oid(git_zstream *stream,
|
|||||||
|
|
||||||
int read_loose_object(const char *path,
|
int read_loose_object(const char *path,
|
||||||
const struct object_id *expected_oid,
|
const struct object_id *expected_oid,
|
||||||
enum object_type *type,
|
struct object_id *real_oid,
|
||||||
unsigned long *size,
|
void **contents,
|
||||||
void **contents)
|
struct object_info *oi)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
void *map = NULL;
|
void *map = NULL;
|
||||||
unsigned long mapsize;
|
unsigned long mapsize;
|
||||||
git_zstream stream;
|
git_zstream stream;
|
||||||
char hdr[MAX_HEADER_LEN];
|
char hdr[MAX_HEADER_LEN];
|
||||||
|
unsigned long *size = oi->sizep;
|
||||||
*contents = NULL;
|
|
||||||
|
|
||||||
map = map_loose_object_1(the_repository, path, NULL, &mapsize);
|
map = map_loose_object_1(the_repository, path, NULL, &mapsize);
|
||||||
if (!map) {
|
if (!map) {
|
||||||
@ -2542,19 +2523,19 @@ int read_loose_object(const char *path,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) {
|
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
|
||||||
|
NULL) < 0) {
|
||||||
error(_("unable to unpack header of %s"), path);
|
error(_("unable to unpack header of %s"), path);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
*type = parse_loose_header(hdr, size);
|
if (parse_loose_header(hdr, oi) < 0) {
|
||||||
if (*type < 0) {
|
|
||||||
error(_("unable to parse header of %s"), path);
|
error(_("unable to parse header of %s"), path);
|
||||||
git_inflate_end(&stream);
|
git_inflate_end(&stream);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*type == OBJ_BLOB && *size > big_file_threshold) {
|
if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
|
||||||
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
|
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else {
|
||||||
@ -2565,10 +2546,7 @@ int read_loose_object(const char *path,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (check_object_signature(the_repository, expected_oid,
|
if (check_object_signature(the_repository, expected_oid,
|
||||||
*contents, *size,
|
*contents, *size, oi->type_name->buf, real_oid)) {
|
||||||
type_name(*type))) {
|
|
||||||
error(_("hash mismatch for %s (expected %s)"), path,
|
|
||||||
oid_to_hex(expected_oid));
|
|
||||||
free(*contents);
|
free(*contents);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the loose object at path, check its hash, and return the contents,
|
* Open the loose object at path, check its hash, and return the contents,
|
||||||
|
* use the "oi" argument to assert things about the object, or e.g. populate its
|
||||||
* type, and size. If the object is a blob, then "contents" may return NULL,
|
* type, and size. If the object is a blob, then "contents" may return NULL,
|
||||||
* to allow streaming of large blobs.
|
* to allow streaming of large blobs.
|
||||||
*
|
*
|
||||||
@ -252,9 +253,9 @@ int force_object_loose(const struct object_id *oid, time_t mtime);
|
|||||||
*/
|
*/
|
||||||
int read_loose_object(const char *path,
|
int read_loose_object(const char *path,
|
||||||
const struct object_id *expected_oid,
|
const struct object_id *expected_oid,
|
||||||
enum object_type *type,
|
struct object_id *real_oid,
|
||||||
unsigned long *size,
|
void **contents,
|
||||||
void **contents);
|
struct object_info *oi);
|
||||||
|
|
||||||
/* Retry packed storage after checking packed and loose storage */
|
/* Retry packed storage after checking packed and loose storage */
|
||||||
#define HAS_OBJECT_RECHECK_PACKED 1
|
#define HAS_OBJECT_RECHECK_PACKED 1
|
||||||
|
4
object.c
4
object.c
@ -279,7 +279,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
|
|||||||
if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
|
if ((obj && obj->type == OBJ_BLOB && repo_has_object_file(r, oid)) ||
|
||||||
(!obj && repo_has_object_file(r, oid) &&
|
(!obj && repo_has_object_file(r, oid) &&
|
||||||
oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
|
oid_object_info(r, oid, NULL) == OBJ_BLOB)) {
|
||||||
if (check_object_signature(r, repl, NULL, 0, NULL) < 0) {
|
if (check_object_signature(r, repl, NULL, 0, NULL, NULL) < 0) {
|
||||||
error(_("hash mismatch %s"), oid_to_hex(oid));
|
error(_("hash mismatch %s"), oid_to_hex(oid));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -290,7 +290,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid)
|
|||||||
buffer = repo_read_object_file(r, oid, &type, &size);
|
buffer = repo_read_object_file(r, oid, &type, &size);
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
if (check_object_signature(r, repl, buffer, size,
|
if (check_object_signature(r, repl, buffer, size,
|
||||||
type_name(type)) < 0) {
|
type_name(type), NULL) < 0) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
error(_("hash mismatch %s"), oid_to_hex(repl));
|
error(_("hash mismatch %s"), oid_to_hex(repl));
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -142,7 +142,8 @@ static int verify_packfile(struct repository *r,
|
|||||||
err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
|
err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
|
||||||
oid_to_hex(&oid), p->pack_name,
|
oid_to_hex(&oid), p->pack_name,
|
||||||
(uintmax_t)entries[i].offset);
|
(uintmax_t)entries[i].offset);
|
||||||
else if (check_object_signature(r, &oid, data, size, type_name(type)))
|
else if (check_object_signature(r, &oid, data, size,
|
||||||
|
type_name(type), NULL))
|
||||||
err = error("packed %s from %s is corrupt",
|
err = error("packed %s from %s is corrupt",
|
||||||
oid_to_hex(&oid), p->pack_name);
|
oid_to_hex(&oid), p->pack_name);
|
||||||
else if (fn) {
|
else if (fn) {
|
||||||
|
27
streaming.c
27
streaming.c
@ -223,19 +223,24 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
|
|||||||
const struct object_id *oid,
|
const struct object_id *oid,
|
||||||
enum object_type *type)
|
enum object_type *type)
|
||||||
{
|
{
|
||||||
|
struct object_info oi = OBJECT_INFO_INIT;
|
||||||
|
oi.sizep = &st->size;
|
||||||
|
oi.typep = type;
|
||||||
|
|
||||||
st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
|
st->u.loose.mapped = map_loose_object(r, oid, &st->u.loose.mapsize);
|
||||||
if (!st->u.loose.mapped)
|
if (!st->u.loose.mapped)
|
||||||
return -1;
|
return -1;
|
||||||
if ((unpack_loose_header(&st->z,
|
switch (unpack_loose_header(&st->z, st->u.loose.mapped,
|
||||||
st->u.loose.mapped,
|
st->u.loose.mapsize, st->u.loose.hdr,
|
||||||
st->u.loose.mapsize,
|
sizeof(st->u.loose.hdr), NULL)) {
|
||||||
st->u.loose.hdr,
|
case ULHR_OK:
|
||||||
sizeof(st->u.loose.hdr)) < 0) ||
|
break;
|
||||||
(parse_loose_header(st->u.loose.hdr, &st->size) < 0)) {
|
case ULHR_BAD:
|
||||||
git_inflate_end(&st->z);
|
case ULHR_TOO_LONG:
|
||||||
munmap(st->u.loose.mapped, st->u.loose.mapsize);
|
goto error;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
if (parse_loose_header(st->u.loose.hdr, &oi) < 0 || *type < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
|
st->u.loose.hdr_used = strlen(st->u.loose.hdr) + 1;
|
||||||
st->u.loose.hdr_avail = st->z.total_out;
|
st->u.loose.hdr_avail = st->z.total_out;
|
||||||
@ -244,6 +249,10 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
|
|||||||
st->read = read_istream_loose;
|
st->read = read_istream_loose;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
error:
|
||||||
|
git_inflate_end(&st->z);
|
||||||
|
munmap(st->u.loose.mapped, st->u.loose.mapsize);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,3 +27,5 @@ numeric sha1:0123456789012345678901234567890123456789
|
|||||||
numeric sha256:0123456789012345678901234567890123456789012345678901234567890123
|
numeric sha256:0123456789012345678901234567890123456789012345678901234567890123
|
||||||
deadbeef sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
deadbeef sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
||||||
deadbeef sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
deadbeef sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
|
||||||
|
deadbeef_short sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbee
|
||||||
|
deadbeef_short sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee
|
||||||
|
@ -315,46 +315,236 @@ test_expect_success '%(deltabase) reports packed delta bases' '
|
|||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
bogus_type="bogus"
|
test_expect_success 'setup bogus data' '
|
||||||
bogus_content="bogus"
|
bogus_short_type="bogus" &&
|
||||||
bogus_size=$(strlen "$bogus_content")
|
bogus_short_content="bogus" &&
|
||||||
bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
|
bogus_short_size=$(strlen "$bogus_short_content") &&
|
||||||
|
bogus_short_sha1=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
|
||||||
|
|
||||||
|
bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
|
||||||
|
bogus_long_content="bogus" &&
|
||||||
|
bogus_long_size=$(strlen "$bogus_long_content") &&
|
||||||
|
bogus_long_sha1=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
|
||||||
|
'
|
||||||
|
|
||||||
|
for arg1 in '' --allow-unknown-type
|
||||||
|
do
|
||||||
|
for arg2 in -s -t -p
|
||||||
|
do
|
||||||
|
if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p"
|
||||||
|
then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" '
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
fatal: invalid object type
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if test "$arg1" = "--allow-unknown-type"
|
||||||
|
then
|
||||||
|
git cat-file $arg1 $arg2 $bogus_short_sha1
|
||||||
|
else
|
||||||
|
test_must_fail git cat-file $arg1 $arg2 $bogus_short_sha1 >out 2>actual &&
|
||||||
|
test_must_be_empty out &&
|
||||||
|
test_cmp expect actual
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "cat-file $arg1 $arg2 error on bogus full OID" '
|
||||||
|
if test "$arg2" = "-p"
|
||||||
|
then
|
||||||
|
cat >expect <<-EOF
|
||||||
|
error: header for $bogus_long_sha1 too long, exceeds 32 bytes
|
||||||
|
fatal: Not a valid object name $bogus_long_sha1
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat >expect <<-EOF
|
||||||
|
error: header for $bogus_long_sha1 too long, exceeds 32 bytes
|
||||||
|
fatal: git cat-file: could not get object info
|
||||||
|
EOF
|
||||||
|
fi &&
|
||||||
|
|
||||||
|
if test "$arg1" = "--allow-unknown-type"
|
||||||
|
then
|
||||||
|
git cat-file $arg1 $arg2 $bogus_short_sha1
|
||||||
|
else
|
||||||
|
test_must_fail git cat-file $arg1 $arg2 $bogus_long_sha1 >out 2>actual &&
|
||||||
|
test_must_be_empty out &&
|
||||||
|
test_cmp expect actual
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "cat-file $arg1 $arg2 error on missing short OID" '
|
||||||
|
cat >expect.err <<-EOF &&
|
||||||
|
fatal: Not a valid object name $(test_oid deadbeef_short)
|
||||||
|
EOF
|
||||||
|
test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
|
||||||
|
test_must_be_empty out
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success "cat-file $arg1 $arg2 error on missing full OID" '
|
||||||
|
if test "$arg2" = "-p"
|
||||||
|
then
|
||||||
|
cat >expect.err <<-EOF
|
||||||
|
fatal: Not a valid object name $(test_oid deadbeef)
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat >expect.err <<-\EOF
|
||||||
|
fatal: git cat-file: could not get object info
|
||||||
|
EOF
|
||||||
|
fi &&
|
||||||
|
test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef) >out 2>err.actual &&
|
||||||
|
test_must_be_empty out &&
|
||||||
|
test_cmp expect.err err.actual
|
||||||
|
'
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
|
||||||
|
git cat-file -e $bogus_short_sha1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '-e can not be combined with --allow-unknown-type' '
|
||||||
|
test_expect_code 128 git cat-file -e --allow-unknown-type $bogus_short_sha1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
|
||||||
|
test_must_fail git cat-file -p $bogus_short_sha1 &&
|
||||||
|
test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_sha1
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '<type> <hash> does not work with objects of broken types' '
|
||||||
|
cat >err.expect <<-\EOF &&
|
||||||
|
fatal: invalid object type "bogus"
|
||||||
|
EOF
|
||||||
|
test_must_fail git cat-file $bogus_short_type $bogus_short_sha1 2>err.actual &&
|
||||||
|
test_cmp err.expect err.actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'broken types combined with --batch and --batch-check' '
|
||||||
|
echo $bogus_short_sha1 >bogus-oid &&
|
||||||
|
|
||||||
|
cat >err.expect <<-\EOF &&
|
||||||
|
fatal: invalid object type
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_must_fail git cat-file --batch <bogus-oid 2>err.actual &&
|
||||||
|
test_cmp err.expect err.actual &&
|
||||||
|
|
||||||
|
test_must_fail git cat-file --batch-check <bogus-oid 2>err.actual &&
|
||||||
|
test_cmp err.expect err.actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'the --batch and --batch-check options do not combine with --allow-unknown-type' '
|
||||||
|
test_expect_code 128 git cat-file --batch --allow-unknown-type <bogus-oid &&
|
||||||
|
test_expect_code 128 git cat-file --batch-check --allow-unknown-type <bogus-oid
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'the --allow-unknown-type option does not consider replacement refs' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
$bogus_short_type
|
||||||
|
EOF
|
||||||
|
git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
# Create it manually, as "git replace" will die on bogus
|
||||||
|
# types.
|
||||||
|
head=$(git rev-parse --verify HEAD) &&
|
||||||
|
test_when_finished "rm -rf .git/refs/replace" &&
|
||||||
|
mkdir -p .git/refs/replace &&
|
||||||
|
echo $head >.git/refs/replace/$bogus_short_sha1 &&
|
||||||
|
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
commit
|
||||||
|
EOF
|
||||||
|
git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success "Type of broken object is correct" '
|
test_expect_success "Type of broken object is correct" '
|
||||||
echo $bogus_type >expect &&
|
echo $bogus_short_type >expect &&
|
||||||
git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
|
git cat-file -t --allow-unknown-type $bogus_short_sha1 >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success "Size of broken object is correct" '
|
test_expect_success "Size of broken object is correct" '
|
||||||
echo $bogus_size >expect &&
|
echo $bogus_short_size >expect &&
|
||||||
git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
|
git cat-file -s --allow-unknown-type $bogus_short_sha1 >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'clean up broken object' '
|
test_expect_success 'clean up broken object' '
|
||||||
rm .git/objects/$(test_oid_to_path $bogus_sha1)
|
rm .git/objects/$(test_oid_to_path $bogus_short_sha1)
|
||||||
'
|
'
|
||||||
|
|
||||||
bogus_type="abcdefghijklmnopqrstuvwxyz1234679"
|
|
||||||
bogus_content="bogus"
|
|
||||||
bogus_size=$(strlen "$bogus_content")
|
|
||||||
bogus_sha1=$(echo_without_newline "$bogus_content" | git hash-object -t $bogus_type --literally -w --stdin)
|
|
||||||
|
|
||||||
test_expect_success "Type of broken object is correct when type is large" '
|
test_expect_success "Type of broken object is correct when type is large" '
|
||||||
echo $bogus_type >expect &&
|
echo $bogus_long_type >expect &&
|
||||||
git cat-file -t --allow-unknown-type $bogus_sha1 >actual &&
|
git cat-file -t --allow-unknown-type $bogus_long_sha1 >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success "Size of large broken object is correct when type is large" '
|
test_expect_success "Size of large broken object is correct when type is large" '
|
||||||
echo $bogus_size >expect &&
|
echo $bogus_long_size >expect &&
|
||||||
git cat-file -s --allow-unknown-type $bogus_sha1 >actual &&
|
git cat-file -s --allow-unknown-type $bogus_long_sha1 >actual &&
|
||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'clean up broken object' '
|
test_expect_success 'clean up broken object' '
|
||||||
rm .git/objects/$(test_oid_to_path $bogus_sha1)
|
rm .git/objects/$(test_oid_to_path $bogus_long_sha1)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cat-file -t and -s on corrupt loose object' '
|
||||||
|
git init --bare corrupt-loose.git &&
|
||||||
|
(
|
||||||
|
cd corrupt-loose.git &&
|
||||||
|
|
||||||
|
# Setup and create the empty blob and its path
|
||||||
|
empty_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$EMPTY_BLOB")) &&
|
||||||
|
git hash-object -w --stdin </dev/null &&
|
||||||
|
|
||||||
|
# Create another blob and its path
|
||||||
|
echo other >other.blob &&
|
||||||
|
other_blob=$(git hash-object -w --stdin <other.blob) &&
|
||||||
|
other_path=$(git rev-parse --git-path objects/$(test_oid_to_path "$other_blob")) &&
|
||||||
|
|
||||||
|
# Before the swap the size is 0
|
||||||
|
cat >out.expect <<-EOF &&
|
||||||
|
0
|
||||||
|
EOF
|
||||||
|
git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
|
||||||
|
test_must_be_empty err.actual &&
|
||||||
|
test_cmp out.expect out.actual &&
|
||||||
|
|
||||||
|
# Swap the two to corrupt the repository
|
||||||
|
mv -f "$other_path" "$empty_path" &&
|
||||||
|
test_must_fail git fsck 2>err.fsck &&
|
||||||
|
grep "hash-path mismatch" err.fsck &&
|
||||||
|
|
||||||
|
# confirm that cat-file is reading the new swapped-in
|
||||||
|
# blob...
|
||||||
|
cat >out.expect <<-EOF &&
|
||||||
|
blob
|
||||||
|
EOF
|
||||||
|
git cat-file -t "$EMPTY_BLOB" >out.actual 2>err.actual &&
|
||||||
|
test_must_be_empty err.actual &&
|
||||||
|
test_cmp out.expect out.actual &&
|
||||||
|
|
||||||
|
# ... since it has a different size now.
|
||||||
|
cat >out.expect <<-EOF &&
|
||||||
|
6
|
||||||
|
EOF
|
||||||
|
git cat-file -s "$EMPTY_BLOB" >out.actual 2>err.actual &&
|
||||||
|
test_must_be_empty err.actual &&
|
||||||
|
test_cmp out.expect out.actual &&
|
||||||
|
|
||||||
|
# So far "cat-file" has been happy to spew the found
|
||||||
|
# content out as-is. Try to make it zlib-invalid.
|
||||||
|
mv -f other.blob "$empty_path" &&
|
||||||
|
test_must_fail git fsck 2>err.fsck &&
|
||||||
|
grep "^error: inflate: data stream error (" err.fsck
|
||||||
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
# Tests for git cat-file --follow-symlinks
|
# Tests for git cat-file --follow-symlinks
|
||||||
|
@ -48,24 +48,70 @@ remove_object () {
|
|||||||
rm "$(sha1_file "$1")"
|
rm "$(sha1_file "$1")"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_expect_success 'object with bad sha1' '
|
test_expect_success 'object with hash mismatch' '
|
||||||
sha=$(echo blob | git hash-object -w --stdin) &&
|
git init --bare hash-mismatch &&
|
||||||
old=$(test_oid_to_path "$sha") &&
|
(
|
||||||
new=$(dirname $old)/$(test_oid ff_2) &&
|
cd hash-mismatch &&
|
||||||
sha="$(dirname $new)$(basename $new)" &&
|
|
||||||
mv .git/objects/$old .git/objects/$new &&
|
|
||||||
test_when_finished "remove_object $sha" &&
|
|
||||||
git update-index --add --cacheinfo 100644 $sha foo &&
|
|
||||||
test_when_finished "git read-tree -u --reset HEAD" &&
|
|
||||||
tree=$(git write-tree) &&
|
|
||||||
test_when_finished "remove_object $tree" &&
|
|
||||||
cmt=$(echo bogus | git commit-tree $tree) &&
|
|
||||||
test_when_finished "remove_object $cmt" &&
|
|
||||||
git update-ref refs/heads/bogus $cmt &&
|
|
||||||
test_when_finished "git update-ref -d refs/heads/bogus" &&
|
|
||||||
|
|
||||||
test_must_fail git fsck 2>out &&
|
oid=$(echo blob | git hash-object -w --stdin) &&
|
||||||
test_i18ngrep "$sha.*corrupt" out
|
oldoid=$oid &&
|
||||||
|
old=$(test_oid_to_path "$oid") &&
|
||||||
|
new=$(dirname $old)/$(test_oid ff_2) &&
|
||||||
|
oid="$(dirname $new)$(basename $new)" &&
|
||||||
|
|
||||||
|
mv objects/$old objects/$new &&
|
||||||
|
git update-index --add --cacheinfo 100644 $oid foo &&
|
||||||
|
tree=$(git write-tree) &&
|
||||||
|
cmt=$(echo bogus | git commit-tree $tree) &&
|
||||||
|
git update-ref refs/heads/bogus $cmt &&
|
||||||
|
|
||||||
|
test_must_fail git fsck 2>out &&
|
||||||
|
grep "$oldoid: hash-path mismatch, found at: .*$new" out
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'object with hash and type mismatch' '
|
||||||
|
git init --bare hash-type-mismatch &&
|
||||||
|
(
|
||||||
|
cd hash-type-mismatch &&
|
||||||
|
|
||||||
|
oid=$(echo blob | git hash-object -w --stdin -t garbage --literally) &&
|
||||||
|
oldoid=$oid &&
|
||||||
|
old=$(test_oid_to_path "$oid") &&
|
||||||
|
new=$(dirname $old)/$(test_oid ff_2) &&
|
||||||
|
oid="$(dirname $new)$(basename $new)" &&
|
||||||
|
|
||||||
|
mv objects/$old objects/$new &&
|
||||||
|
git update-index --add --cacheinfo 100644 $oid foo &&
|
||||||
|
tree=$(git write-tree) &&
|
||||||
|
cmt=$(echo bogus | git commit-tree $tree) &&
|
||||||
|
git update-ref refs/heads/bogus $cmt &&
|
||||||
|
|
||||||
|
|
||||||
|
test_must_fail git fsck 2>out &&
|
||||||
|
grep "^error: $oldoid: hash-path mismatch, found at: .*$new" out &&
|
||||||
|
grep "^error: $oldoid: object is of unknown type '"'"'garbage'"'"'" out
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success POSIXPERM 'zlib corrupt loose object output ' '
|
||||||
|
git init --bare corrupt-loose-output &&
|
||||||
|
(
|
||||||
|
cd corrupt-loose-output &&
|
||||||
|
oid=$(git hash-object -w --stdin --literally </dev/null) &&
|
||||||
|
oidf=objects/$(test_oid_to_path "$oid") &&
|
||||||
|
chmod 755 $oidf &&
|
||||||
|
echo extra garbage >>$oidf &&
|
||||||
|
|
||||||
|
cat >expect.error <<-EOF &&
|
||||||
|
error: garbage at end of loose object '\''$oid'\''
|
||||||
|
error: unable to unpack contents of ./$oidf
|
||||||
|
error: $oid: object corrupt or missing: ./$oidf
|
||||||
|
EOF
|
||||||
|
test_must_fail git fsck 2>actual &&
|
||||||
|
grep ^error: actual >error &&
|
||||||
|
test_cmp expect.error error
|
||||||
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'branch pointing to non-commit' '
|
test_expect_success 'branch pointing to non-commit' '
|
||||||
@ -865,4 +911,21 @@ test_expect_success 'detect corrupt index file in fsck' '
|
|||||||
test_i18ngrep "bad index file" errors
|
test_i18ngrep "bad index file" errors
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fsck error and recovery on invalid object type' '
|
||||||
|
git init --bare garbage-type &&
|
||||||
|
(
|
||||||
|
cd garbage-type &&
|
||||||
|
|
||||||
|
garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
|
||||||
|
|
||||||
|
cat >err.expect <<-\EOF &&
|
||||||
|
fatal: invalid object type
|
||||||
|
EOF
|
||||||
|
test_must_fail git fsck >out 2>err &&
|
||||||
|
grep -e "^error" -e "^fatal" err >errors &&
|
||||||
|
test_line_count = 1 errors &&
|
||||||
|
grep "$garbage_blob: object is of unknown type '"'"'garbage'"'"':" err
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user