summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--archive-tar.c31
-rwxr-xr-xt/t5000-tar-tree.sh4
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