diff options
-rw-r--r-- | archive-tar.c | 31 | ||||
-rwxr-xr-x | t/t5000-tar-tree.sh | 4 |
2 files changed, 31 insertions, 4 deletions
diff --git a/archive-tar.c b/archive-tar.c index cb99df2814..57a15406d9 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -18,6 +18,13 @@ static int tar_umask = 002; static int write_tar_filter_archive(const struct archiver *ar, struct archiver_args *args); +/* + * This is the max value that a ustar size header can specify, as it is fixed + * at 11 octal digits. POSIX specifies that we switch to extended headers at + * this size. + */ +#define USTAR_MAX_SIZE 077777777777UL + /* writes out the whole block, but only if it is full */ static void write_if_needed(void) { @@ -137,6 +144,20 @@ static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, strbuf_addch(sb, '\n'); } +/* + * Like strbuf_append_ext_header, but for numeric values. + */ +static void strbuf_append_ext_header_uint(struct strbuf *sb, + const char *keyword, + uintmax_t value) +{ + char buf[40]; /* big enough for 2^128 in decimal, plus NUL */ + int len; + + len = xsnprintf(buf, sizeof(buf), "%"PRIuMAX, value); + strbuf_append_ext_header(sb, keyword, buf, len); +} + static unsigned int ustar_header_chksum(const struct ustar_header *header) { const unsigned char *p = (const unsigned char *)header; @@ -208,7 +229,7 @@ static int write_tar_entry(struct archiver_args *args, struct ustar_header header; struct strbuf ext_header = STRBUF_INIT; unsigned int old_mode = mode; - unsigned long size; + unsigned long size, size_in_header; void *buffer; int err = 0; @@ -267,7 +288,13 @@ static int write_tar_entry(struct archiver_args *args, memcpy(header.linkname, buffer, size); } - prepare_header(args, &header, mode, size); + size_in_header = size; + if (S_ISREG(mode) && size > USTAR_MAX_SIZE) { + size_in_header = 0; + strbuf_append_ext_header_uint(&ext_header, "size", size); + } + + prepare_header(args, &header, mode, size_in_header); if (ext_header.len > 0) { err = write_extended_header(args, sha1, ext_header.buf, diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 950bdd31e7..93c2d3405d 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -360,7 +360,7 @@ test_expect_success 'set up repository with huge blob' ' # We expect git to die with SIGPIPE here (otherwise we # would generate the whole 64GB). -test_expect_failure 'generate tar with huge size' ' +test_expect_success 'generate tar with huge size' ' { git archive HEAD echo $? >exit-code @@ -369,7 +369,7 @@ test_expect_failure 'generate tar with huge size' ' test_cmp expect exit-code ' -test_expect_failure TAR_HUGE 'system tar can read our huge size' ' +test_expect_success TAR_HUGE 'system tar can read our huge size' ' echo 68719476737 >expect && tar_info huge.tar | cut -d" " -f1 >actual && test_cmp expect actual |