summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--changelogs/fragments/unarchive_timestamp_t32.yaml3
-rw-r--r--lib/ansible/modules/unarchive.py22
-rw-r--r--test/units/modules/test_unarchive.py10
3 files changed, 34 insertions, 1 deletions
diff --git a/changelogs/fragments/unarchive_timestamp_t32.yaml b/changelogs/fragments/unarchive_timestamp_t32.yaml
new file mode 100644
index 0000000000..969c85de05
--- /dev/null
+++ b/changelogs/fragments/unarchive_timestamp_t32.yaml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+ - unarchive - Clamp timestamps from beyond y2038 to representible values when unpacking zip files on platforms that use 32-bit time_t (e.g. Debian i386).
diff --git a/lib/ansible/modules/unarchive.py b/lib/ansible/modules/unarchive.py
index 0b192ab569..b317dbc737 100644
--- a/lib/ansible/modules/unarchive.py
+++ b/lib/ansible/modules/unarchive.py
@@ -241,6 +241,7 @@ uid:
import binascii
import codecs
+import ctypes
import fnmatch
import grp
import os
@@ -262,6 +263,13 @@ from ansible.module_utils.urls import fetch_file
from shlex import quote
from zipfile import BadZipFile
+try:
+ from functools import cache
+except ImportError:
+ # Python < 3.9
+ from functools import lru_cache
+ cache = lru_cache(maxsize=None)
+
# String from tar that shows the tar contents are different from the
# filesystem
OWNER_DIFF_RE = re.compile(r': Uid differs$')
@@ -279,6 +287,18 @@ CONTENT_DIFF_RE = re.compile(r': Contents differ$')
SIZE_DIFF_RE = re.compile(r': Size differs$')
+@cache
+def _y2038_impacted():
+ """Determine if the system has 64-bit time_t."""
+ if hasattr(ctypes, "c_time_t"): # Python >= 3.12
+ return ctypes.sizeof(ctypes.c_time_t) < 8
+ try:
+ time.gmtime(2**31)
+ except OverflowError:
+ return True
+ return False
+
+
def crc32(path, buffer_size):
""" Return a CRC32 checksum of a file """
@@ -414,6 +434,8 @@ class ZipArchive(object):
try:
if int(match.groups()[0]) < 1980:
date_time = epoch_date_time
+ elif int(match.groups()[0]) >= 2038 and _y2038_impacted():
+ date_time = (2038, 1, 1, 0, 0, 0, 0, 0, 0)
elif int(match.groups()[0]) > 2107:
date_time = (2107, 12, 31, 23, 59, 59, 0, 0, 0)
else:
diff --git a/test/units/modules/test_unarchive.py b/test/units/modules/test_unarchive.py
index 6a2f0d9a67..b1885c2f1c 100644
--- a/test/units/modules/test_unarchive.py
+++ b/test/units/modules/test_unarchive.py
@@ -14,6 +14,14 @@ def fake_ansible_module():
return FakeAnsibleModule()
+def max_zip_timestamp():
+ """Return the max clamp value that will be selected."""
+ try:
+ return time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0)))
+ except OverflowError:
+ return time.mktime(time.struct_time((2038, 1, 1, 0, 0, 0, 0, 0, 0)))
+
+
class FakeAnsibleModule:
def __init__(self):
self.params = {}
@@ -68,7 +76,7 @@ class TestCaseZipArchive:
),
pytest.param(
"21081231.000000",
- time.mktime(time.struct_time((2107, 12, 31, 23, 59, 59, 0, 0, 0))),
+ max_zip_timestamp(),
id="invalid-year-2108",
),
pytest.param(