Merge branch 'rs/archive-zip-omit-out-of-range-mtime'

The ZIP archive creation code has been updated to omit the 'UT'
extended timestamp extra field when the commit time exceeds the
32-bit positive signed integer limit, avoiding integer wraparound
for dates past the year 2038.

* rs/archive-zip-omit-out-of-range-mtime:
  archive-zip: omit UT extra field if mtime is out of range
This commit is contained in:
Junio C Hamano committed 2026-10-29 07:18:00 -07:00
commit dd2e9fb957
2 files changed
+43 -22

No files matched your search

+15 -22
View File
@@ -24,6 +24,7 @@ static int zip_time;
/* We only care about the "buf" part here. */
static struct strbuf zip_dir;
static struct strbuf zip_extra_mtime;
static uintmax_t zip_offset;
static uint64_t zip_dir_entries;
@@ -81,14 +82,6 @@ struct zip_dir_trailer {
unsigned char _end[1];
};
struct zip_extra_mtime {
unsigned char magic[2];
unsigned char extra_size[2];
unsigned char flags[1];
unsigned char mtime[4];
unsigned char _end[1];
};
struct zip64_extra {
unsigned char magic[2];
unsigned char extra_size[2];
@@ -129,9 +122,6 @@ struct zip64_dir_trailer_locator {
#define ZIP64_DATA_DESC_SIZE offsetof(struct zip64_data_desc, _end)
#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end)
#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end)
#define ZIP_EXTRA_MTIME_SIZE offsetof(struct zip_extra_mtime, _end)
#define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \
(ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags))
#define ZIP64_EXTRA_SIZE offsetof(struct zip64_extra, _end)
#define ZIP64_EXTRA_PAYLOAD_SIZE \
(ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size))
@@ -299,9 +289,8 @@ static int write_zip_entry(struct archiver_args *args,
{
struct zip_local_header header;
uintmax_t offset = zip_offset;
struct zip_extra_mtime extra;
struct zip64_extra extra64;
size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE;
size_t header_extra_size = zip_extra_mtime.len;
int need_zip64_extra = 0;
unsigned long attr2;
unsigned long compressed_size;
@@ -315,7 +304,7 @@ static int write_zip_entry(struct archiver_args *args,
const char *path_without_prefix = path + args->baselen;
unsigned int creator_version = 0;
unsigned int version_needed = 10;
size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE;
size_t zip_dir_extra_size = zip_extra_mtime.len;
size_t zip64_dir_extra_payload_size = 0;
crc = crc32(0, NULL, 0);
@@ -381,11 +370,6 @@ static int write_zip_entry(struct archiver_args *args,
}
}
copy_le16(extra.magic, 0x5455);
copy_le16(extra.extra_size, ZIP_EXTRA_MTIME_PAYLOAD_SIZE);
extra.flags[0] = 1; /* just mtime */
copy_le32(extra.mtime, args->time);
if (size > 0xffffffff || compressed_size > 0xffffffff)
need_zip64_extra = 1;
if (stream && size > 0x7fffffff)
@@ -412,8 +396,8 @@ static int write_zip_entry(struct archiver_args *args,
zip_offset += ZIP_LOCAL_HEADER_SIZE;
write_or_die(1, path, pathlen);
zip_offset += pathlen;
write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE);
zip_offset += ZIP_EXTRA_MTIME_SIZE;
write_or_die(1, zip_extra_mtime.buf, zip_extra_mtime.len);
zip_offset += zip_extra_mtime.len;
if (need_zip64_extra) {
copy_le16(extra64.magic, 0x0001);
copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE);
@@ -546,7 +530,7 @@ static int write_zip_entry(struct archiver_args *args,
strbuf_add_le(&zip_dir, 4, attr2);
strbuf_add_le(&zip_dir, 4, clamp32(offset));
strbuf_add(&zip_dir, path, pathlen);
strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE);
strbuf_addbuf(&zip_dir, &zip_extra_mtime);
if (zip64_dir_extra_payload_size) {
strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */
strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size);
@@ -644,12 +628,21 @@ static int write_zip_archive(const struct archiver *ar UNUSED,
dos_time(&args->time, &zip_date, &zip_time);
strbuf_init(&zip_dir, 0);
strbuf_init(&zip_extra_mtime, 0);
if (args->time <= 0x7fffffff) {
strbuf_add_le(&zip_extra_mtime, 2, 0x5455); /* magic */
strbuf_add_le(&zip_extra_mtime, 2, 1 + 4); /* payload size */
strbuf_add_le(&zip_extra_mtime, 1, 1); /* flags; just mtime */
strbuf_add_le(&zip_extra_mtime, 4, args->time);
}
err = write_archive_entries(args, write_zip_entry);
if (!err)
write_zip_trailer(args->commit_oid);
strbuf_release(&zip_dir);
strbuf_release(&zip_extra_mtime);
return err;
}
+28
View File
@@ -233,4 +233,32 @@ test_expect_success 'tar archive with long paths' '
git archive -o long_paths.tar $tree
'
test_lazy_prereq ZIPINFO_UT_EXTRA '
"$ZIPINFO" -v "$TEST_DIRECTORY"/t5004/big-pack.zip |
grep "UT extra field modtime"
'
test_expect_success TIME_IS_64BIT 'zip archive with 32-bit mtime' '
blob=$(echo foo | git hash-object -w --stdin) &&
tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
git archive --mtime="@2147483647 +0000" -o with_ut_extra.zip $tree
'
test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'zip archive with mtime exceeding 32-bit' '
blob=$(echo foo | git hash-object -w --stdin) &&
tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
git archive --mtime="@2147483648 +0000" -o without_ut_extra.zip $tree
'
test_expect_success TIME_IS_64BIT,ZIPINFO_UT_EXTRA 'UT extra field' '
"$ZIPINFO" -v with_ut_extra.zip >actual &&
test_grep "UT extra field modtime" actual
'
test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT,ZIPINFO_UT_EXTRA \
'UT extra field omitted when mtime exceeds 32-bit' '
"$ZIPINFO" -v without_ut_extra.zip >actual &&
test_grep ! "UT extra field modtime" actual
'
test_done