Skip to content

Commit 6e8e099

Browse files
peffgitster
authored andcommitted
archive-tar: write extended headers for far-future mtime
The ustar format represents timestamps as seconds since the epoch, but only has room to store 11 octal digits. To express anything larger, we need to use an extended header. This is exactly the same case we fixed for the size field in the previous commit, and the solution here follows the same pattern. This is even mentioned as an issue in f2f0267 (archive-tar: use xsnprintf for trivial formatting, 2015-09-24), but since it only affected things far in the future, it wasn't deemed worth dealing with. But note that my calculations claiming thousands of years were off there; because our xsnprintf produces a NUL byte, we only have until the year 2242 to fix this. Given that this is just around the corner (geologically speaking, anyway), and because it's easy to fix, let's just make it work. Unlike the previous fix for "size", where we had to write an individual extended header for each file, we can write one global header (since we have only one mtime for the whole archive). There's a slight bit of trickiness there. We may already be writing a global header with a "comment" field for the commit sha1. So we need to write our new field into the same header. To do this, we push the decision of whether to write such a header down into write_global_extended_header(), which will now assemble the header as it sees fit, and will return early if we have nothing to write (in practice, we'll only have a large mtime if it comes from a commit, but this makes it also work if you set your system clock ahead such that time() returns a huge value). Note that we don't (and never did) handle negative timestamps (i.e., before 1970). This would probably not be too hard to support in the same way, but since git does not support negative timestamps at all, I didn't bother here. After writing the extended header, we munge the timestamp in the ustar headers to the maximum-allowable size. This is wrong, but it's the least-wrong thing we can provide to a tar implementation that doesn't understand pax headers (it's also what GNU tar does). Helped-by: René Scharfe <[email protected]> Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d1657b5 commit 6e8e099

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

archive-tar.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ static int write_tar_filter_archive(const struct archiver *ar,
2222
* This is the max value that a ustar size header can specify, as it is fixed
2323
* at 11 octal digits. POSIX specifies that we switch to extended headers at
2424
* this size.
25+
*
26+
* Likewise for the mtime (which happens to use a buffer of the same size).
2527
*/
2628
#define USTAR_MAX_SIZE 077777777777UL
29+
#define USTAR_MAX_MTIME 077777777777UL
2730

2831
/* writes out the whole block, but only if it is full */
2932
static void write_if_needed(void)
@@ -324,7 +327,18 @@ static int write_global_extended_header(struct archiver_args *args)
324327
unsigned int mode;
325328
int err = 0;
326329

327-
strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
330+
if (sha1)
331+
strbuf_append_ext_header(&ext_header, "comment",
332+
sha1_to_hex(sha1), 40);
333+
if (args->time > USTAR_MAX_MTIME) {
334+
strbuf_append_ext_header_uint(&ext_header, "mtime",
335+
args->time);
336+
args->time = USTAR_MAX_MTIME;
337+
}
338+
339+
if (!ext_header.len)
340+
return 0;
341+
328342
memset(&header, 0, sizeof(header));
329343
*header.typeflag = TYPEFLAG_GLOBAL_HEADER;
330344
mode = 0100666;
@@ -409,8 +423,7 @@ static int write_tar_archive(const struct archiver *ar,
409423
{
410424
int err = 0;
411425

412-
if (args->commit_sha1)
413-
err = write_global_extended_header(args);
426+
err = write_global_extended_header(args);
414427
if (!err)
415428
err = write_archive_entries(args, write_tar_entry);
416429
if (!err)

t/t5000-tar-tree.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,11 +383,11 @@ test_expect_success 'set up repository with far-future commit' '
383383
git commit -m "tempori parendum"
384384
'
385385

386-
test_expect_failure 'generate tar with future mtime' '
386+
test_expect_success 'generate tar with future mtime' '
387387
git archive HEAD >future.tar
388388
'
389389

390-
test_expect_failure TAR_HUGE 'system tar can read our future mtime' '
390+
test_expect_success TAR_HUGE 'system tar can read our future mtime' '
391391
echo 4147 >expect &&
392392
tar_info future.tar | cut -d" " -f2 >actual &&
393393
test_cmp expect actual

0 commit comments

Comments
 (0)