From 4fdfd1f62a9d50298b0bb71e8bea04174af4a3ab Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Wed, 24 Jul 2019 13:36:50 +0200 Subject: Fix processing of tar mtime on 32 bit systems struct stat uses time_t to store time values. On some 32 bit systems, this may be a 32 bit integer. This patch adds a broken-out 64 bit time value to tar_header_decoded_t and makes sure to clamp the value to +/- (2^32 - 1) if required when writing it back to a struct stat. Reported-by: Matt Turner Signed-off-by: David Oberhollenzer --- configure.ac | 3 + include/tar.h | 4 ++ lib/tar/read_header.c | 19 +++++-- m4/m4_ax_compile_check_sizeof.m4 | 115 +++++++++++++++++++++++++++++++++++++++ tests/tar_gnu.c | 13 ++++- tests/tar_pax.c | 12 +++- tests/tar_ustar.c | 13 ++++- tests/tar_xattr_bsd.c | 1 + tests/tar_xattr_schily.c | 1 + 9 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 m4/m4_ax_compile_check_sizeof.m4 diff --git a/configure.ac b/configure.ac index 1141236..870c572 100644 --- a/configure.ac +++ b/configure.ac @@ -156,6 +156,9 @@ if test "x$have_compressor" != "xyes"; then AC_MSG_ERROR([no compressor available. At lest one is required]) fi +##### additional checks ##### +AX_COMPILE_CHECK_SIZEOF(time_t) + ##### generate output ##### AC_CONFIG_HEADERS([config.h]) diff --git a/include/tar.h b/include/tar.h index 5ac0763..e6a506a 100644 --- a/include/tar.h +++ b/include/tar.h @@ -74,6 +74,10 @@ typedef struct { uint64_t record_size; bool unknown_record; tar_xattr_t *xattr; + + /* broken out since struct stat could contain + 32 bit values on 32 bit systems. */ + int64_t mtime; } tar_header_decoded_t; #define TAR_TYPE_FILE '0' diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c index 043b5ff..a083c2f 100644 --- a/lib/tar/read_header.c +++ b/lib/tar/read_header.c @@ -129,11 +129,11 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, if (line[6] == '-') { if (pax_read_decimal(line + 7, &field)) goto fail; - out->sb.st_mtime = -((int64_t)field); + out->mtime = -((int64_t)field); } else { if (pax_read_decimal(line + 6, &field)) goto fail; - out->sb.st_mtime = field; + out->mtime = field; } *set_by_pax |= PAX_MTIME; } else if (!strncmp(line, "GNU.sparse.name=", 16)) { @@ -284,9 +284,9 @@ static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, return -1; if (field & 0x8000000000000000UL) { field = ~field + 1; - out->sb.st_mtime = -((int64_t)field); + out->mtime = -((int64_t)field); } else { - out->sb.st_mtime = field; + out->mtime = field; } } @@ -338,6 +338,17 @@ static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, break; } +#if SIZEOF_TIME_T < 8 + if (out->mtime > (int64_t)INT32_MAX) { + out->sb.st_mtime = INT32_MAX; + } else if (out->mtime < (int64_t)INT32_MIN) { + out->sb.st_mtime = INT32_MIN; + } else { + out->sb.st_mtime = out->mtime; + } +#else + out->sb.st_mtime = out->mtime; +#endif return 0; } diff --git a/m4/m4_ax_compile_check_sizeof.m4 b/m4/m4_ax_compile_check_sizeof.m4 new file mode 100644 index 0000000..f834df6 --- /dev/null +++ b/m4/m4_ax_compile_check_sizeof.m4 @@ -0,0 +1,115 @@ +# ============================================================================ +# https://www.gnu.org/software/autoconf-archive/ax_compile_check_sizeof.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_COMPILE_CHECK_SIZEOF(TYPE [, HEADERS [, EXTRA_SIZES...]]) +# +# DESCRIPTION +# +# This macro checks for the size of TYPE using compile checks, not run +# checks. You can supply extra HEADERS to look into. the check will cycle +# through 1 2 4 8 16 and any EXTRA_SIZES the user supplies. If a match is +# found, it will #define SIZEOF_`TYPE' to that value. Otherwise it will +# emit a configure time error indicating the size of the type could not be +# determined. +# +# The trick is that C will not allow duplicate case labels. While this is +# valid C code: +# +# switch (0) case 0: case 1:; +# +# The following is not: +# +# switch (0) case 0: case 0:; +# +# Thus, the AC_COMPILE_IFELSE will fail if the currently tried size does +# not match. +# +# Here is an example skeleton configure.in script, demonstrating the +# macro's usage: +# +# AC_PROG_CC +# AC_CHECK_HEADERS(stddef.h unistd.h) +# AC_TYPE_SIZE_T +# AC_CHECK_TYPE(ssize_t, int) +# +# headers='#ifdef HAVE_STDDEF_H +# #include +# #endif +# #ifdef HAVE_UNISTD_H +# #include +# #endif +# ' +# +# AX_COMPILE_CHECK_SIZEOF(char) +# AX_COMPILE_CHECK_SIZEOF(short) +# AX_COMPILE_CHECK_SIZEOF(int) +# AX_COMPILE_CHECK_SIZEOF(long) +# AX_COMPILE_CHECK_SIZEOF(unsigned char *) +# AX_COMPILE_CHECK_SIZEOF(void *) +# AX_COMPILE_CHECK_SIZEOF(size_t, $headers) +# AX_COMPILE_CHECK_SIZEOF(ssize_t, $headers) +# AX_COMPILE_CHECK_SIZEOF(ptrdiff_t, $headers) +# AX_COMPILE_CHECK_SIZEOF(off_t, $headers) +# +# LICENSE +# +# Copyright (c) 2008 Kaveh Ghazi +# Copyright (c) 2017 Reini Urban +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 8 + +AU_ALIAS([AC_COMPILE_CHECK_SIZEOF], [AX_COMPILE_CHECK_SIZEOF]) +AC_DEFUN([AX_COMPILE_CHECK_SIZEOF], +[changequote(<<, >>)dnl +dnl The name to #define. +define(<>, translit(sizeof_$1, [a-z *], [A-Z_P]))dnl +dnl The cache variable name. +define(<>, translit(ac_cv_sizeof_$1, [ *], [_p]))dnl +changequote([, ])dnl +AC_MSG_CHECKING(size of $1) +AC_CACHE_VAL(AC_CV_NAME, +[for ac_size in 4 8 1 2 16 $3 ; do # List sizes in rough order of prevalence. + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +$2 +]], [[switch (0) case 0: case (sizeof ($1) == $ac_size):;]])], [AC_CV_NAME=$ac_size]) + if test x$AC_CV_NAME != x ; then break; fi +done +]) +if test x$AC_CV_NAME = x ; then + AC_MSG_ERROR([cannot determine a size for $1]) +fi +AC_MSG_RESULT($AC_CV_NAME) +AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME, [The number of bytes in type $1]) +undefine([AC_TYPE_NAME])dnl +undefine([AC_CV_NAME])dnl +]) diff --git a/tests/tar_gnu.c b/tests/tar_gnu.c index c5f8ddb..4f78da7 100644 --- a/tests/tar_gnu.c +++ b/tests/tar_gnu.c @@ -48,6 +48,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542905892); + assert(hdr.mtime == 1542905892); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data0", fd, buffer, 5) == 0); @@ -63,6 +64,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 013375560044); + assert(hdr.mtime == 013375560044); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data1", fd, buffer, 5) == 0); @@ -78,6 +80,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 8589934592); assert(hdr.sb.st_mtime == 013375730126); + assert(hdr.mtime == 013375730126); assert(strcmp(hdr.name, "big-file.bin") == 0); assert(!hdr.unknown_record); clear_header(&hdr); @@ -90,6 +93,7 @@ int main(void) assert(hdr.sb.st_gid == 0x80000000); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 013376036700); + assert(hdr.mtime == 013376036700); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data2", fd, buffer, 5) == 0); @@ -104,7 +108,12 @@ int main(void) assert(hdr.sb.st_uid == 01750); assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); - assert(hdr.sb.st_mtime == 8589934592); +#if SIZEOF_TIME_T < 8 + assert(hdr.sb.st_mtime == INT32_MAX); +#else + assert(hdr.sb.st_mtime == 8589934592L); +#endif + assert(hdr.mtime == 8589934592L); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data3", fd, buffer, 5) == 0); @@ -120,6 +129,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == -315622800); + assert(hdr.mtime == -315622800); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data4", fd, buffer, 5) == 0); @@ -135,6 +145,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542909670); + assert(hdr.mtime == 1542909670); assert(strcmp(hdr.name, filename) == 0); assert(!hdr.unknown_record); assert(read_data("data5", fd, buffer, 5) == 0); diff --git a/tests/tar_pax.c b/tests/tar_pax.c index c383427..9ca12ff 100644 --- a/tests/tar_pax.c +++ b/tests/tar_pax.c @@ -48,6 +48,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542905892); + assert(hdr.mtime == 1542905892); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data0", fd, buffer, 5) == 0); @@ -63,6 +64,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 8589934592); assert(hdr.sb.st_mtime == 1542959190); + assert(hdr.mtime == 1542959190); assert(strcmp(hdr.name, "big-file.bin") == 0); assert(!hdr.unknown_record); clear_header(&hdr); @@ -75,6 +77,7 @@ int main(void) assert(hdr.sb.st_gid == 2147483648); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 013376036700); + assert(hdr.mtime == 013376036700); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data1", fd, buffer, 5) == 0); @@ -89,7 +92,12 @@ int main(void) assert(hdr.sb.st_uid == 01750); assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); - assert(hdr.sb.st_mtime == 8589934592); +#if SIZEOF_TIME_T < 8 + assert(hdr.sb.st_mtime == INT32_MAX); +#else + assert(hdr.sb.st_mtime == 8589934592L); +#endif + assert(hdr.mtime == 8589934592L); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data2", fd, buffer, 5) == 0); @@ -105,6 +113,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == -315622800); + assert(hdr.mtime == -315622800); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data3", fd, buffer, 5) == 0); @@ -120,6 +129,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542909670); + assert(hdr.mtime == 1542909670); assert(strcmp(hdr.name, filename) == 0); assert(!hdr.unknown_record); assert(read_data("data4", fd, buffer, 5) == 0); diff --git a/tests/tar_ustar.c b/tests/tar_ustar.c index d497b15..695665f 100644 --- a/tests/tar_ustar.c +++ b/tests/tar_ustar.c @@ -48,6 +48,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542905892); + assert(hdr.mtime == 1542905892); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data0", fd, buffer, 5) == 0); @@ -63,6 +64,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542905892); + assert(hdr.mtime == 1542905892); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data1", fd, buffer, 5) == 0); @@ -78,6 +80,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542905892); + assert(hdr.mtime == 1542905892); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data2", fd, buffer, 5) == 0); @@ -93,6 +96,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 8589934592); assert(hdr.sb.st_mtime == 013375730126); + assert(hdr.mtime == 013375730126); assert(strcmp(hdr.name, "big-file.bin") == 0); assert(!hdr.unknown_record); clear_header(&hdr); @@ -105,6 +109,7 @@ int main(void) assert(hdr.sb.st_gid == 8388608); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 013376036700); + assert(hdr.mtime == 013376036700); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data3", fd, buffer, 5) == 0); @@ -119,7 +124,12 @@ int main(void) assert(hdr.sb.st_uid == 01750); assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); - assert(hdr.sb.st_mtime == 8589934592); +#if SIZEOF_TIME_T < 8 + assert(hdr.sb.st_mtime == INT32_MAX); +#else + assert(hdr.sb.st_mtime == 8589934592L); +#endif + assert(hdr.mtime == 8589934592L); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data4", fd, buffer, 5) == 0); @@ -135,6 +145,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1542909670); + assert(hdr.mtime == 1542909670); assert(strcmp(hdr.name, filename) == 0); assert(!hdr.unknown_record); assert(read_data("data5", fd, buffer, 5) == 0); diff --git a/tests/tar_xattr_bsd.c b/tests/tar_xattr_bsd.c index 360448f..b2a1290 100644 --- a/tests/tar_xattr_bsd.c +++ b/tests/tar_xattr_bsd.c @@ -43,6 +43,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1543094477); + assert(hdr.mtime == 1543094477); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data0", fd, buffer, 5) == 0); diff --git a/tests/tar_xattr_schily.c b/tests/tar_xattr_schily.c index e49bdd9..9030a45 100644 --- a/tests/tar_xattr_schily.c +++ b/tests/tar_xattr_schily.c @@ -43,6 +43,7 @@ int main(void) assert(hdr.sb.st_gid == 01750); assert(hdr.sb.st_size == 5); assert(hdr.sb.st_mtime == 1543094477); + assert(hdr.mtime == 1543094477); assert(strcmp(hdr.name, "input.txt") == 0); assert(!hdr.unknown_record); assert(read_data("data0", fd, buffer, 5) == 0); -- cgit v1.2.3