Skip to content

Commit

Permalink
Merge branch 'jc/hash-object'
Browse files Browse the repository at this point in the history
"hash-object --literally" introduced in v2.2 was not prepared to
take a really long object type name.

* jc/hash-object:
  write_sha1_file(): do not use a separate sha1[] array
  t1007: add hash-object --literally tests
  hash-object --literally: fix buffer overrun with extra-long object type
  git-hash-object.txt: document --literally option
  • Loading branch information
gitster committed May 11, 2015
2 parents b9f5d38 + 1427a7f commit 051086b
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 9 deletions.
10 changes: 8 additions & 2 deletions Documentation/git-hash-object.txt
Expand Up @@ -9,7 +9,7 @@ git-hash-object - Compute object ID and optionally creates a blob from a file
SYNOPSIS
--------
[verse]
'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin [--literally]] [--] <file>...
'git hash-object' [-t <type>] [-w] --stdin-paths [--no-filters] < <list-of-paths>

DESCRIPTION
Expand Down Expand Up @@ -51,7 +51,13 @@ OPTIONS
Hash the contents as is, ignoring any input filter that would
have been chosen by the attributes mechanism, including the end-of-line
conversion. If the file is read from standard input then this
is always implied, unless the --path option is given.
is always implied, unless the `--path` option is given.

--literally::
Allow `--stdin` to hash any garbage into a loose object which might not
otherwise pass standard object parsing or git-fsck checks. Useful for
stress-testing Git itself or reproducing characteristics of corrupt or
bogus objects encountered in the wild.

GIT
---
Expand Down
4 changes: 1 addition & 3 deletions builtin/hash-object.c
Expand Up @@ -22,10 +22,8 @@ static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigne

if (strbuf_read(&buf, fd, 4096) < 0)
ret = -1;
else if (flags & HASH_WRITE_OBJECT)
ret = write_sha1_file(buf.buf, buf.len, type, sha1);
else
ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags);
strbuf_release(&buf);
return ret;
}
Expand Down
1 change: 1 addition & 0 deletions cache.h
Expand Up @@ -909,6 +909,7 @@ static inline const unsigned char *lookup_replace_object_extended(const unsigned
extern int sha1_object_info(const unsigned char *, unsigned long *);
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
extern int git_open_noatime(const char *name);
Expand Down
26 changes: 22 additions & 4 deletions sha1_file.c
Expand Up @@ -3007,23 +3007,41 @@ static int freshen_packed_object(const unsigned char *sha1)
return 1;
}

int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)
{
unsigned char sha1[20];
char hdr[32];
int hdrlen;

/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
*/
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
if (returnsha1)
hashcpy(returnsha1, sha1);
if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
return 0;
return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
}

int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
unsigned char *sha1, unsigned flags)
{
char *header;
int hdrlen, status = 0;

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

if (!(flags & HASH_WRITE_OBJECT))
goto cleanup;
if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
goto cleanup;
status = write_loose_object(sha1, header, hdrlen, buf, len, 0);

cleanup:
free(header);
return status;
}

int force_object_loose(const unsigned char *sha1, time_t mtime)
{
void *buf;
Expand Down
11 changes: 11 additions & 0 deletions t/t1007-hash-object.sh
Expand Up @@ -209,4 +209,15 @@ 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_done

0 comments on commit 051086b

Please sign in to comment.