Merge branch 'jk/no-funny-object-types'

Support to create a loose object file with unknown object type has
been dropped.

* jk/no-funny-object-types:
  object-file: drop support for writing objects with unknown types
  hash-object: handle --literally with OPT_NEGBIT
  hash-object: merge HASH_* and INDEX_* flags
  hash-object: stop allowing unknown types
  t: add lib-loose.sh
  t/helper: add zlib test-tool
  oid_object_info(): drop type_name strbuf
  fsck: stop using object_info->type_name strbuf
  oid_object_info_convert(): stop using string for object type
  cat-file: use type enum instead of buffer for -t option
  object-file: drop OBJECT_INFO_ALLOW_UNKNOWN_TYPE flag
  cat-file: make --allow-unknown-type a noop
  object-file.h: fix typo in variable declaration
maint
Junio C Hamano 2025-05-27 13:59:08 -07:00
commit 17d9dbd3c2
20 changed files with 223 additions and 450 deletions

View File

@ -9,8 +9,7 @@ SYNOPSIS
--------
[verse]
'git cat-file' <type> <object>
'git cat-file' (-e | -p) <object>
'git cat-file' (-t | -s) [--allow-unknown-type] <object>
'git cat-file' (-e | -p | -t | -s) <object>
'git cat-file' (--textconv | --filters)
[<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
@ -202,9 +201,6 @@ flush::
only once, even if it is stored multiple times in the
repository.

--allow-unknown-type::
Allow `-s` or `-t` to query broken/corrupt objects of unknown type.

--follow-symlinks::
With `--batch` or `--batch-check`, follow symlinks inside the
repository when requesting objects with extended SHA-1

View File

@ -859,6 +859,7 @@ TEST_BUILTINS_OBJS += test-wildmatch.o
TEST_BUILTINS_OBJS += test-windows-named-pipe.o
TEST_BUILTINS_OBJS += test-write-cache.o
TEST_BUILTINS_OBJS += test-xml-encode.o
TEST_BUILTINS_OBJS += test-zlib.o

# Do not add more tests here unless they have extra dependencies. Add
# them in TEST_BUILTINS_OBJS above.

View File

@ -100,8 +100,7 @@ static int stream_blob(const struct object_id *oid)
return 0;
}

static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
int unknown_type)
static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
{
int ret;
struct object_id oid;
@ -110,7 +109,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
unsigned long size;
struct object_context obj_context = {0};
struct object_info oi = OBJECT_INFO_INIT;
struct strbuf sb = STRBUF_INIT;
unsigned flags = OBJECT_INFO_LOOKUP_REPLACE;
unsigned get_oid_flags =
GET_OID_RECORD_PATH |
@ -121,9 +119,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (!path && opt_cw)
get_oid_flags |= GET_OID_REQUIRE_PATH;

if (unknown_type)
flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE;

if (get_oid_with_context(the_repository, obj_name, get_oid_flags, &oid,
&obj_context))
die("Not a valid object name %s", obj_name);
@ -136,16 +131,12 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
buf = NULL;
switch (opt) {
case 't':
oi.type_name = &sb;
oi.typep = &type;
if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0)
die("git cat-file: could not get object info");
if (sb.len) {
printf("%s\n", sb.buf);
strbuf_release(&sb);
ret = 0;
goto cleanup;
}
break;
printf("%s\n", type_name(type));
ret = 0;
goto cleanup;

case 's':
oi.sizep = &size;
@ -1038,8 +1029,7 @@ int cmd_cat_file(int argc,

const char * const builtin_catfile_usage[] = {
N_("git cat-file <type> <object>"),
N_("git cat-file (-e | -p) <object>"),
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
N_("git cat-file (-e | -p | -t | -s) <object>"),
N_("git cat-file (--textconv | --filters)\n"
" [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
@ -1057,8 +1047,8 @@ int cmd_cat_file(int argc,
OPT_GROUP(N_("Emit [broken] object attributes")),
OPT_CMDMODE('t', NULL, &opt, N_("show object type (one of 'blob', 'tree', 'commit', 'tag', ...)"), 't'),
OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'),
OPT_BOOL(0, "allow-unknown-type", &unknown_type,
N_("allow -s and -t to work with broken/corrupt objects")),
OPT_HIDDEN_BOOL(0, "allow-unknown-type", &unknown_type,
N_("historical option -- no-op")),
OPT_BOOL(0, "use-mailmap", &use_mailmap, N_("use mail map file")),
OPT_ALIAS(0, "mailmap", "use-mailmap"),
/* Batch mode */
@ -1209,10 +1199,7 @@ int cmd_cat_file(int argc,
obj_name = argv[1];
}

if (unknown_type && opt != 't' && opt != 's')
die("git cat-file --allow-unknown-type: use with -s or -t");

ret = cat_one_file(opt, exp_type, obj_name, unknown_type);
ret = cat_one_file(opt, exp_type, obj_name);

out:
list_objects_filter_release(&batch.objects_filter);

View File

@ -614,12 +614,11 @@ 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 UNUSED)
{
struct for_each_loose_cb *cb_data = data;
struct object *obj;
enum object_type type = OBJ_NONE;
unsigned long size;
@ -629,8 +628,6 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
struct object_id real_oid = *null_oid(the_hash_algo);
int err = 0;

strbuf_reset(&cb_data->obj_type);
oi.type_name = &cb_data->obj_type;
oi.sizep = &size;
oi.typep = &type;

@ -642,10 +639,6 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
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;
free(contents);
@ -697,7 +690,6 @@ static void fsck_object_dir(const char *path)
{
struct progress *progress = NULL;
struct for_each_loose_cb cb_data = {
.obj_type = STRBUF_INIT,
.progress = progress,
};

@ -712,7 +704,6 @@ static void fsck_object_dir(const char *path)
&cb_data);
display_progress(progress, 256);
stop_progress(&progress);
strbuf_release(&cb_data.obj_type);
}

static int fsck_head_link(const char *head_ref_name,

View File

@ -19,49 +19,15 @@
#include "strbuf.h"
#include "write-or-die.h"

enum {
HASH_OBJECT_CHECK = (1 << 0),
HASH_OBJECT_WRITE = (1 << 1),
};

/*
* This is to create corrupt objects for debugging and as such it
* needs to bypass the data conversion performed by, and the type
* limitation imposed by, index_fd() and its callees.
*/
static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)
static void hash_fd(int fd, const char *type, const char *path, unsigned flags)
{
struct strbuf buf = STRBUF_INIT;
int ret;

if (strbuf_read(&buf, fd, 4096) < 0)
ret = -1;
else
ret = write_object_file_literally(buf.buf, buf.len, type, oid,
(flags & HASH_OBJECT_WRITE) ? WRITE_OBJECT_FILE_PERSIST : 0);
close(fd);
strbuf_release(&buf);
return ret;
}

static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
int literally)
{
unsigned int index_flags = 0;
struct stat st;
struct object_id oid;

if (flags & HASH_OBJECT_WRITE)
index_flags |= INDEX_WRITE_OBJECT;
if (flags & HASH_OBJECT_CHECK)
index_flags |= INDEX_FORMAT_CHECK;

if (fstat(fd, &st) < 0 ||
(literally
? hash_literally(&oid, fd, type, flags)
: index_fd(the_repository->index, &oid, fd, &st,
type_from_string(type), path, index_flags)))
die((flags & HASH_OBJECT_WRITE)
index_fd(the_repository->index, &oid, fd, &st,
type_from_string(type), path, flags))
die((flags & INDEX_WRITE_OBJECT)
? "Unable to add %s to database"
: "Unable to hash %s", path);
printf("%s\n", oid_to_hex(&oid));
@ -69,15 +35,14 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
}

static void hash_object(const char *path, const char *type, const char *vpath,
unsigned flags, int literally)
unsigned flags)
{
int fd;
fd = xopen(path, O_RDONLY);
hash_fd(fd, type, vpath, flags, literally);
hash_fd(fd, type, vpath, flags);
}

static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
int literally)
static void hash_stdin_paths(const char *type, int no_filters, unsigned flags)
{
struct strbuf buf = STRBUF_INIT;
struct strbuf unquoted = STRBUF_INIT;
@ -89,8 +54,7 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,
die("line is badly quoted");
strbuf_swap(&buf, &unquoted);
}
hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,
literally);
hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags);
}
strbuf_release(&buf);
strbuf_release(&unquoted);
@ -111,19 +75,20 @@ int cmd_hash_object(int argc,
int hashstdin = 0;
int stdin_paths = 0;
int no_filters = 0;
int literally = 0;
int nongit = 0;
unsigned flags = HASH_OBJECT_CHECK;
unsigned flags = INDEX_FORMAT_CHECK;
const char *vpath = NULL;
char *vpath_free = NULL;
const struct option hash_object_options[] = {
OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
OPT_BIT('w', NULL, &flags, N_("write the object into the object database"),
HASH_OBJECT_WRITE),
INDEX_WRITE_OBJECT),
OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
OPT_BOOL( 0, "literally", &literally, N_("just hash any random garbage to create corrupt objects for debugging Git")),
OPT_NEGBIT( 0, "literally", &flags,
N_("just hash any random garbage to create corrupt objects for debugging Git"),
INDEX_FORMAT_CHECK),
OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
OPT_END()
};
@ -133,7 +98,7 @@ int cmd_hash_object(int argc,
argc = parse_options(argc, argv, prefix, hash_object_options,
hash_object_usage, 0);

if (flags & HASH_OBJECT_WRITE)
if (flags & INDEX_WRITE_OBJECT)
prefix = setup_git_directory();
else
prefix = setup_git_directory_gently(&nongit);
@ -169,7 +134,7 @@ int cmd_hash_object(int argc,
}

if (hashstdin)
hash_fd(0, type, vpath, flags, literally);
hash_fd(0, type, vpath, flags);

for (i = 0 ; i < argc; i++) {
const char *arg = argv[i];
@ -178,12 +143,12 @@ int cmd_hash_object(int argc,
if (prefix)
arg = to_free = prefix_filename(prefix, arg);
hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
flags, literally);
flags);
free(to_free);
}

if (stdin_paths)
hash_stdin_paths(type, no_filters, flags, literally);
hash_stdin_paths(type, no_filters, flags);

free(vpath_free);


View File

@ -130,12 +130,6 @@ int has_loose_object(const struct object_id *oid)
return check_and_freshen(oid, 0);
}

static int format_object_header_literally(char *str, size_t size,
const char *type, size_t objsize)
{
return xsnprintf(str, size, "%s %"PRIuMAX, type, (uintmax_t)objsize) + 1;
}

int format_object_header(char *str, size_t size, enum object_type type,
size_t objsize)
{
@ -144,7 +138,7 @@ int format_object_header(char *str, size_t size, enum object_type type,
if (!name)
BUG("could not get a type name for 'enum object_type' value %d", type);

return format_object_header_literally(str, size, name, objsize);
return xsnprintf(str, size, "%s %"PRIuMAX, name, (uintmax_t)objsize) + 1;
}

int check_object_signature(struct repository *r, const struct object_id *oid,
@ -299,8 +293,7 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
unsigned char *map,
unsigned long mapsize,
void *buffer,
unsigned long bufsiz,
struct strbuf *header)
unsigned long bufsiz)
{
int status;

@ -325,32 +318,9 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
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".
* We have a header longer than MAX_HEADER_LEN.
*/
if (!header)
return ULHR_TOO_LONG;

/*
* buffer[0..bufsiz] was not large enough. Copy the partial
* result out to header, and then append the result of further
* reading the stream.
*/
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);

do {
stream->next_out = buffer;
stream->avail_out = bufsiz;

obj_read_unlock();
status = git_inflate(stream, 0);
obj_read_lock();
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
return 0;
} while (status == Z_OK);
return ULHR_BAD;
return ULHR_TOO_LONG;
}

static void *unpack_loose_rest(git_zstream *stream,
@ -427,8 +397,6 @@ int parse_loose_header(const char *hdr, struct object_info *oi)
}

type = type_from_string_gently(type_buf, type_len, 1);
if (oi->type_name)
strbuf_add(oi->type_name, type_buf, type_len);
if (oi->typep)
*oi->typep = type;

@ -476,10 +444,8 @@ int loose_object_info(struct repository *r,
void *map;
git_zstream stream;
char hdr[MAX_HEADER_LEN];
struct strbuf hdrbuf = STRBUF_INIT;
unsigned long size_scratch;
enum object_type type_scratch;
int allow_unknown = flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE;

if (oi->delta_base_oid)
oidclr(oi->delta_base_oid, the_repository->hash_algo);
@ -492,7 +458,7 @@ int loose_object_info(struct repository *r,
* return value implicitly indicates whether the
* object even exists.
*/
if (!oi->typep && !oi->type_name && !oi->sizep && !oi->contentp) {
if (!oi->typep && !oi->sizep && !oi->contentp) {
struct stat st;
if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
return quick_has_loose(r, oid) ? 0 : -1;
@ -521,18 +487,15 @@ int loose_object_info(struct repository *r,
if (oi->disk_sizep)
*oi->disk_sizep = mapsize;

switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
allow_unknown ? &hdrbuf : NULL)) {
switch (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr))) {
case ULHR_OK:
if (parse_loose_header(hdrbuf.len ? hdrbuf.buf : hdr, oi) < 0)
if (parse_loose_header(hdr, oi) < 0)
status = error(_("unable to parse %s header"), oid_to_hex(oid));
else if (!allow_unknown && *oi->typep < 0)
else if (*oi->typep < 0)
die(_("invalid object type"));

if (!oi->contentp)
break;
if (hdrbuf.len)
BUG("unpacking content with unknown types not yet supported");
*oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
if (*oi->contentp)
goto cleanup;
@ -558,7 +521,6 @@ cleanup:
munmap(map, mapsize);
if (oi->sizep == &size_scratch)
oi->sizep = NULL;
strbuf_release(&hdrbuf);
if (oi->typep == &type_scratch)
oi->typep = NULL;
oi->whence = OI_LOOSE;
@ -590,17 +552,6 @@ static void write_object_file_prepare(const struct git_hash_algo *algo,
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}

static void write_object_file_prepare_literally(const struct git_hash_algo *algo,
const void *buf, unsigned long len,
const char *type, struct object_id *oid,
char *hdr, int *hdrlen)
{
struct git_hash_ctx c;

*hdrlen = format_object_header_literally(hdr, *hdrlen, type, len);
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}

#define CHECK_COLLISION_DEST_VANISHED -2

static int check_collision(const char *source, const char *dest)
@ -730,21 +681,14 @@ out:
return 0;
}

static void hash_object_file_literally(const struct git_hash_algo *algo,
const void *buf, unsigned long len,
const char *type, struct object_id *oid)
{
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);

write_object_file_prepare_literally(algo, buf, len, type, oid, hdr, &hdrlen);
}

void hash_object_file(const struct git_hash_algo *algo, const void *buf,
unsigned long len, enum object_type type,
struct object_id *oid)
{
hash_object_file_literally(algo, buf, len, type_name(type), oid);
char hdr[MAX_HEADER_LEN];
int hdrlen = sizeof(hdr);

write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen);
}

/* Finalize a file on disk, and close it. */
@ -1146,53 +1090,6 @@ int write_object_file_flags(const void *buf, unsigned long len,
return 0;
}

int write_object_file_literally(const void *buf, unsigned long len,
const char *type, struct object_id *oid,
unsigned flags)
{
char *header;
struct repository *repo = the_repository;
const struct git_hash_algo *algo = repo->hash_algo;
const struct git_hash_algo *compat = repo->compat_hash_algo;
struct object_id compat_oid;
int hdrlen, status = 0;
int compat_type = -1;

if (compat) {
compat_type = type_from_string_gently(type, -1, 1);
if (compat_type == OBJ_BLOB)
hash_object_file(compat, buf, len, compat_type,
&compat_oid);
else if (compat_type != -1) {
struct strbuf converted = STRBUF_INIT;
convert_object_file(the_repository,
&converted, algo, compat,
buf, len, compat_type, 0);
hash_object_file(compat, converted.buf, converted.len,
compat_type, &compat_oid);
strbuf_release(&converted);
}
}

/* type string, SP, %lu of the length plus NUL must fit this */
hdrlen = strlen(type) + MAX_HEADER_LEN;
header = xmalloc(hdrlen);
write_object_file_prepare_literally(the_hash_algo, buf, len, type,
oid, header, &hdrlen);

if (!(flags & WRITE_OBJECT_FILE_PERSIST))
goto cleanup;
if (freshen_packed_object(oid) || freshen_loose_object(oid))
goto cleanup;
status = write_loose_object(oid, header, hdrlen, buf, len, 0, 0);
if (compat_type != -1)
return repo_add_loose_object_map(repo, oid, &compat_oid);

cleanup:
free(header);
return status;
}

int force_object_loose(const struct object_id *oid, time_t mtime)
{
struct repository *repo = the_repository;
@ -1682,8 +1579,7 @@ int read_loose_object(const char *path,
goto out;
}

if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
NULL) != ULHR_OK) {
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr)) != ULHR_OK) {
error(_("unable to unpack header of %s"), path);
goto out_inflate;
}
@ -1693,6 +1589,12 @@ int read_loose_object(const char *path,
goto out_inflate;
}

if (*oi->typep < 0) {
error(_("unable to parse type from header '%s' of %s"),
hdr, path);
goto out_inflate;
}

if (*oi->typep == OBJ_BLOB &&
*size > repo_settings_get_big_file_threshold(the_repository)) {
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
@ -1703,9 +1605,9 @@ int read_loose_object(const char *path,
error(_("unable to unpack contents of %s"), path);
goto out_inflate;
}
hash_object_file_literally(the_repository->hash_algo,
*contents, *size,
oi->type_name->buf, real_oid);
hash_object_file(the_repository->hash_algo,
*contents, *size,
*oi->typep, real_oid);
if (!oideq(expected_oid, real_oid))
goto out_inflate;
}

View File

@ -133,12 +133,7 @@ int format_object_header(char *str, size_t size, enum object_type type,
* - 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.
* It will only parse up to MAX_HEADER_LEN bytes.
*/
enum unpack_loose_header_result {
ULHR_OK,
@ -149,8 +144,7 @@ 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);
unsigned long bufsiz);

/**
* parse_loose_header() parses the starting "<type> <len>\0" of an
@ -165,7 +159,7 @@ int parse_loose_header(const char *hdr, struct object_info *oi);

enum {
/*
* By default, `write_object_file_literally()` does not actually write
* By default, `write_object_file()` does not actually write
* anything into the object store, but only computes the object ID.
* This flag changes that so that the object will be written as a loose
* object and persisted.
@ -180,7 +174,7 @@ enum {

int write_object_file_flags(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid,
struct object_id *comapt_oid_in, unsigned flags);
struct object_id *compat_oid_in, unsigned flags);
static inline int write_object_file(const void *buf, unsigned long len,
enum object_type type, struct object_id *oid)
{
@ -193,9 +187,6 @@ struct input_stream {
int is_finished;
};

int write_object_file_literally(const void *buf, unsigned long len,
const char *type, struct object_id *oid,
unsigned flags);
int stream_loose_object(struct input_stream *in_stream, size_t len,
struct object_id *oid);


View File

@ -646,8 +646,6 @@ static int do_oid_object_info_extended(struct repository *r,
*(oi->disk_sizep) = 0;
if (oi->delta_base_oid)
oidclr(oi->delta_base_oid, the_repository->hash_algo);
if (oi->type_name)
strbuf_addstr(oi->type_name, type_name(co->type));
if (oi->contentp)
*oi->contentp = xmemdupz(co->buf, co->size);
oi->whence = OI_CACHED;
@ -727,7 +725,7 @@ static int oid_object_info_convert(struct repository *r,
{
const struct git_hash_algo *input_algo = &hash_algos[input_oid->algo];
int do_die = flags & OBJECT_INFO_DIE_IF_CORRUPT;
struct strbuf type_name = STRBUF_INIT;
enum object_type type;
struct object_id oid, delta_base_oid;
struct object_info new_oi, *oi;
unsigned long size;
@ -753,7 +751,7 @@ static int oid_object_info_convert(struct repository *r,
if (input_oi->sizep || input_oi->contentp) {
new_oi.contentp = &content;
new_oi.sizep = &size;
new_oi.type_name = &type_name;
new_oi.typep = &type;
}
oi = &new_oi;
}
@ -766,12 +764,7 @@ static int oid_object_info_convert(struct repository *r,

if (new_oi.contentp) {
struct strbuf outbuf = STRBUF_INIT;
enum object_type type;

type = type_from_string_gently(type_name.buf, type_name.len,
!do_die);
if (type == -1)
return -1;
if (type != OBJ_BLOB) {
ret = convert_object_file(the_repository, &outbuf,
the_hash_algo, input_algo,
@ -788,10 +781,8 @@ static int oid_object_info_convert(struct repository *r,
*input_oi->contentp = content;
else
free(content);
if (input_oi->type_name)
*input_oi->type_name = type_name;
else
strbuf_release(&type_name);
if (input_oi->typep)
*input_oi->typep = type;
}
if (new_oi.delta_base_oid == &delta_base_oid) {
if (repo_oid_to_algop(r, &delta_base_oid, input_algo,

View File

@ -206,7 +206,6 @@ struct object_info {
unsigned long *sizep;
off_t *disk_sizep;
struct object_id *delta_base_oid;
struct strbuf *type_name;
void **contentp;

/* Response */
@ -241,8 +240,6 @@ struct object_info {

/* Invoke lookup_replace_object() on the given hash */
#define OBJECT_INFO_LOOKUP_REPLACE 1
/* Allow reading from a loose object file of unknown/bogus type */
#define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
/* Do not retry packed storage after checking packed and loose storage */
#define OBJECT_INFO_QUICK 8
/*

View File

@ -1598,17 +1598,12 @@ int packed_object_info(struct repository *r, struct packed_git *p,
*oi->disk_sizep = pack_pos_to_offset(p, pos + 1) - obj_offset;
}

if (oi->typep || oi->type_name) {
if (oi->typep) {
enum object_type ptot;
ptot = packed_to_object_type(r, p, obj_offset,
type, &w_curs, curpos);
if (oi->typep)
*oi->typep = ptot;
if (oi->type_name) {
const char *tn = type_name(ptot);
if (tn)
strbuf_addstr(oi->type_name, tn);
}
if (ptot < 0) {
type = OBJ_BAD;
goto out;

View File

@ -238,7 +238,7 @@ static int open_istream_loose(struct git_istream *st, struct repository *r,
return -1;
switch (unpack_loose_header(&st->z, st->u.loose.mapped,
st->u.loose.mapsize, st->u.loose.hdr,
sizeof(st->u.loose.hdr), NULL)) {
sizeof(st->u.loose.hdr))) {
case ULHR_OK:
break;
case ULHR_BAD:

View File

@ -77,6 +77,7 @@ test_tool_sources = [
'test-windows-named-pipe.c',
'test-write-cache.c',
'test-xml-encode.c',
'test-zlib.c',
]

test_tool = executable('test-tool',

View File

@ -91,6 +91,7 @@ static struct test_cmd cmds[] = {
{ "windows-named-pipe", cmd__windows_named_pipe },
#endif
{ "write-cache", cmd__write_cache },
{ "zlib", cmd__zlib },
};

static NORETURN void die_usage(void)

View File

@ -84,6 +84,7 @@ int cmd__wildmatch(int argc, const char **argv);
int cmd__windows_named_pipe(int argc, const char **argv);
#endif
int cmd__write_cache(int argc, const char **argv);
int cmd__zlib(int argc, const char **argv);

int cmd_hash_impl(int ac, const char **av, int algo, int unsafe);


62
t/helper/test-zlib.c Normal file
View File

@ -0,0 +1,62 @@
#include "test-tool.h"
#include "git-zlib.h"
#include "strbuf.h"

static const char *zlib_usage = "test-tool zlib [inflate|deflate]";

static void do_zlib(struct git_zstream *stream,
int (*zlib_func)(git_zstream *, int),
int fd_in, int fd_out)
{
struct strbuf buf_in = STRBUF_INIT;
int status = Z_OK;

if (strbuf_read(&buf_in, fd_in, 0) < 0)
die_errno("read error");

stream->next_in = (unsigned char *)buf_in.buf;
stream->avail_in = buf_in.len;

while (status == Z_OK ||
(status == Z_BUF_ERROR && !stream->avail_out)) {
unsigned char buf_out[4096];

stream->next_out = buf_out;
stream->avail_out = sizeof(buf_out);

status = zlib_func(stream, Z_FINISH);
if (write_in_full(fd_out, buf_out,
sizeof(buf_out) - stream->avail_out) < 0)
die_errno("write error");
}

if (status != Z_STREAM_END)
die("zlib error %d", status);

strbuf_release(&buf_in);
}

int cmd__zlib(int argc, const char **argv)
{
git_zstream stream;

if (argc != 2)
usage(zlib_usage);

memset(&stream, 0, sizeof(stream));

if (!strcmp(argv[1], "inflate")) {
git_inflate_init(&stream);
do_zlib(&stream, git_inflate, 0, 1);
git_inflate_end(&stream);
} else if (!strcmp(argv[1], "deflate")) {
git_deflate_init(&stream, Z_DEFAULT_COMPRESSION);
do_zlib(&stream, git_deflate, 0, 1);
git_deflate_end(&stream);
} else {
error("unknown mode: %s", argv[1]);
usage(zlib_usage);
}

return 0;
}

30
t/lib-loose.sh Normal file
View File

@ -0,0 +1,30 @@
# Support routines for hand-crafting loose objects.

# Write a loose object into the odb at $1, with object type $2 and contents
# from stdin. Writes the oid to stdout. Example:
#
# oid=$(echo foo | loose_obj .git/objects blob)
#
loose_obj () {
cat >tmp_loose.content &&
size=$(wc -c <tmp_loose.content) &&
{
# Do not quote $size here; we want the shell
# to strip whitespace that "wc" adds on some platforms.
printf "%s %s\0" "$2" $size &&
cat tmp_loose.content
} >tmp_loose.raw &&

oid=$(test-tool $test_hash_algo <tmp_loose.raw) &&
suffix=${oid#??} &&
prefix=${oid%$suffix} &&
dir=$1/$prefix &&
file=$dir/$suffix &&

test-tool zlib deflate <tmp_loose.raw >tmp_loose.zlib &&
mkdir -p "$dir" &&
mv tmp_loose.zlib "$file" &&

rm tmp_loose.raw tmp_loose.content &&
echo "$oid"
}

View File

@ -3,6 +3,7 @@
test_description='git cat-file'

. ./test-lib.sh
. "$TEST_DIRECTORY/lib-loose.sh"

test_cmdmode_usage () {
test_expect_code 129 "$@" 2>err &&
@ -136,18 +137,6 @@ $content"
test_cmp expect actual
'

test_expect_success "Type of $type is correct using --allow-unknown-type" '
echo $type >expect &&
git cat-file -t --allow-unknown-type $oid >actual &&
test_cmp expect actual
'

test_expect_success "Size of $type is correct using --allow-unknown-type" '
echo $size >expect &&
git cat-file -s --allow-unknown-type $oid >actual &&
test_cmp expect actual
'

test -z "$content" ||
test_expect_success "Content of $type is correct" '
echo_without_newline "$content" >expect &&
@ -669,103 +658,75 @@ test_expect_success 'setup bogus data' '
bogus_short_type="bogus" &&
bogus_short_content="bogus" &&
bogus_short_size=$(strlen "$bogus_short_content") &&
bogus_short_oid=$(echo_without_newline "$bogus_short_content" | git hash-object -t $bogus_short_type --literally -w --stdin) &&
bogus_short_oid=$(echo_without_newline "$bogus_short_content" | loose_obj .git/objects $bogus_short_type) &&

bogus_long_type="abcdefghijklmnopqrstuvwxyz1234679" &&
bogus_long_content="bogus" &&
bogus_long_size=$(strlen "$bogus_long_content") &&
bogus_long_oid=$(echo_without_newline "$bogus_long_content" | git hash-object -t $bogus_long_type --literally -w --stdin)
bogus_long_oid=$(echo_without_newline "$bogus_long_content" | loose_obj .git/objects $bogus_long_type)
'

for arg1 in '' --allow-unknown-type
for arg1 in -s -t -p
do
for arg2 in -s -t -p
do
if test "$arg1" = "--allow-unknown-type" && test "$arg2" = "-p"
test_expect_success "cat-file $arg1 error on bogus short OID" '
cat >expect <<-\EOF &&
fatal: invalid object type
EOF

test_must_fail git cat-file $arg1 $bogus_short_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
'

test_expect_success "cat-file $arg1 error on bogus full OID" '
if test "$arg1" = "-p"
then
continue
fi


test_expect_success "cat-file $arg1 $arg2 error on bogus short OID" '
cat >expect <<-\EOF &&
fatal: invalid object type
cat >expect <<-EOF
error: header for $bogus_long_oid too long, exceeds 32 bytes
fatal: Not a valid object name $bogus_long_oid
EOF

if test "$arg1" = "--allow-unknown-type"
then
git cat-file $arg1 $arg2 $bogus_short_oid
else
test_must_fail git cat-file $arg1 $arg2 $bogus_short_oid >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_oid too long, exceeds 32 bytes
fatal: Not a valid object name $bogus_long_oid
EOF
else
cat >expect <<-EOF
error: header for $bogus_long_oid 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_oid
else
test_must_fail git cat-file $arg1 $arg2 $bogus_long_oid >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)
else
cat >expect <<-EOF
error: header for $bogus_long_oid too long, exceeds 32 bytes
fatal: git cat-file: could not get object info
EOF
test_must_fail git cat-file $arg1 $arg2 $(test_oid deadbeef_short) >out 2>err.actual &&
test_must_be_empty out &&
test_cmp expect.err err.actual
'
fi &&

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
test_must_fail git cat-file $arg1 $bogus_long_oid >out 2>actual &&
test_must_be_empty out &&
test_cmp expect actual
'

test_expect_success "cat-file $arg1 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 $(test_oid deadbeef_short) >out 2>err.actual &&
test_must_be_empty out &&
test_cmp expect.err err.actual
'

test_expect_success "cat-file $arg1 error on missing full OID" '
if test "$arg1" = "-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 $(test_oid deadbeef) >out 2>err.actual &&
test_must_be_empty out &&
test_cmp expect.err err.actual
'
done

test_expect_success '-e is OK with a broken object without --allow-unknown-type' '
test_expect_success '-e is OK with a broken object' '
git cat-file -e $bogus_short_oid
'

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_oid
'

test_expect_success '-p cannot print a broken object even with --allow-unknown-type' '
test_must_fail git cat-file -p $bogus_short_oid &&
test_expect_code 128 git cat-file -p --allow-unknown-type $bogus_short_oid
'

test_expect_success '<type> <hash> does not work with objects of broken types' '
cat >err.expect <<-\EOF &&
fatal: invalid object type "bogus"
@ -788,60 +749,8 @@ test_expect_success 'broken types combined with --batch and --batch-check' '
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_oid >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 "test-tool ref-store main delete-refs 0 msg refs/replace/$bogus_short_oid" &&
test-tool ref-store main update-ref msg "refs/replace/$bogus_short_oid" $head $ZERO_OID REF_SKIP_OID_VERIFICATION &&

cat >expect <<-EOF &&
commit
EOF
git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'

test_expect_success "Type of broken object is correct" '
echo $bogus_short_type >expect &&
git cat-file -t --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'

test_expect_success "Size of broken object is correct" '
echo $bogus_short_size >expect &&
git cat-file -s --allow-unknown-type $bogus_short_oid >actual &&
test_cmp expect actual
'

test_expect_success 'clean up broken object' '
rm .git/objects/$(test_oid_to_path $bogus_short_oid)
'

test_expect_success "Type of broken object is correct when type is large" '
echo $bogus_long_type >expect &&
git cat-file -t --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'

test_expect_success "Size of large broken object is correct when type is large" '
echo $bogus_long_size >expect &&
git cat-file -s --allow-unknown-type $bogus_long_oid >actual &&
test_cmp expect actual
'

test_expect_success 'clean up broken object' '
test_expect_success 'clean up broken objects' '
rm .git/objects/$(test_oid_to_path $bogus_short_oid) &&
rm .git/objects/$(test_oid_to_path $bogus_long_oid)
'

@ -903,25 +812,6 @@ test_expect_success 'cat-file -t and -s on corrupt loose object' '
)
'

test_expect_success 'truncated object with --allow-unknown-type' - <<\EOT
objtype='a really long type name that exceeds the 32-byte limit' &&
blob=$(git hash-object -w --literally -t "$objtype" /dev/null) &&
objpath=.git/objects/$(test_oid_to_path "$blob") &&

# We want to truncate the object far enough in that we don't hit the
# end while inflating the first 32 bytes (since we want to have to dig
# for the trailing NUL of the header). But we don't want to go too far,
# since our header isn't very big. And of course we are counting
# deflated zlib bytes in the on-disk file, so it's a bit of a guess.
# Empirically 50 seems to work.
mv "$objpath" obj.bak &&
test_when_finished 'mv obj.bak "$objpath"' &&
test_copy_bytes 50 <obj.bak >"$objpath" &&

test_must_fail git cat-file --allow-unknown-type -t $blob 2>err &&
test_grep "unable to unpack $blob header" err
EOT

test_expect_success 'object reading handles zlib dictionary' - <<\EOT
echo 'content that will be recompressed' >file &&
blob=$(git hash-object -w file) &&

View File

@ -248,15 +248,8 @@ test_expect_success 'hash-object complains about truncated type name' '
test_must_fail git hash-object -t bl --stdin </dev/null
'

test_expect_success '--literally' '
t=1234567890 &&
echo example | git hash-object -t $t --literally --stdin
'

test_expect_success '--literally with extra-long type' '
t=12345678901234567890123456789012345678901234567890 &&
t="$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t$t" &&
echo example | git hash-object -t $t --literally --stdin
test_expect_success '--literally complains about non-standard types' '
test_must_fail git hash-object -t bogus --literally --stdin
'

test_expect_success '--stdin outside of repository (uses SHA-1)' '

View File

@ -7,6 +7,7 @@ test_description='git fsck random collection of tests
'

. ./test-lib.sh
. "$TEST_DIRECTORY/lib-loose.sh"

test_expect_success setup '
git config gc.auto 0 &&
@ -71,30 +72,6 @@ test_expect_success 'object with hash mismatch' '
)
'

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 'zlib corrupt loose object output ' '
git init --bare corrupt-loose-output &&
(
@ -997,12 +974,13 @@ test_expect_success 'fsck error and recovery on invalid object type' '
(
cd garbage-type &&

garbage_blob=$(git hash-object --stdin -w -t garbage --literally </dev/null) &&
garbage_blob=$(loose_obj objects garbage </dev/null) &&

test_must_fail git fsck 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_line_count = 2 errors &&
test_grep "unable to parse type from header .garbage" err &&
test_grep "$garbage_blob: object corrupt or missing:" err
)
'


View File

@ -24,6 +24,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

. ./test-lib.sh
. "$TEST_DIRECTORY/lib-loose.sh"

test_cmp_failed_rev_parse () {
dir=$1
@ -67,8 +68,8 @@ test_expect_success 'ambiguous loose bad object parsed as OBJ_BAD' '
cd blob.bad &&

# Both have the prefix "bad0"
echo xyzfaowcoh | git hash-object -t bad -w --stdin --literally &&
echo xyzhjpyvwl | git hash-object -t bad -w --stdin --literally
echo xyzfaowcoh | loose_obj objects bad &&
echo xyzhjpyvwl | loose_obj objects bad
) &&

test_cmp_failed_rev_parse blob.bad bad0 <<-\EOF