From 3e1094ec6460013d6bc907e310b107719cb6446b Mon Sep 17 00:00:00 2001 From: sthen Date: Tue, 1 Mar 2022 21:19:11 +0000 Subject: [PATCH] Support mtime/atime/ctime extended headers in !SMALL builds. These are becoming quite common in distributed software (including tars produced by Python and Go) and often standard timestamps are not set, resulting in extracted files dated as the epoch. Lots of help from tb@, ok tb@ millert@ --- bin/pax/tar.c | 70 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/bin/pax/tar.c b/bin/pax/tar.c index 9d8a92d9d13..686ec524f83 100644 --- a/bin/pax/tar.c +++ b/bin/pax/tar.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tar.c,v 1.69 2021/06/14 00:36:13 deraadt Exp $ */ +/* $OpenBSD: tar.c,v 1.70 2022/03/01 21:19:11 sthen Exp $ */ /* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */ /*- @@ -415,6 +415,7 @@ tar_rd(ARCHD *arcn, char *buf) else arcn->sb.st_mtime = val; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + arcn->sb.st_ctimensec = arcn->sb.st_atimensec = arcn->sb.st_mtimensec; /* * have to look at the last character, it may be a '/' and that is used @@ -784,12 +785,21 @@ reset: arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & 0xfff); arcn->sb.st_size = (off_t)asc_ull(hd->size, sizeof(hd->size), OCT); - val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT); - if (val > MAX_TIME_T) - arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ - else - arcn->sb.st_mtime = val; - arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + if (arcn->sb.st_mtime == 0) { + val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT); + if (val > MAX_TIME_T) + arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ + else + arcn->sb.st_mtime = val; + } + if (arcn->sb.st_ctime == 0) { + arcn->sb.st_ctime = arcn->sb.st_mtime; + arcn->sb.st_ctimensec = arcn->sb.st_mtimensec; + } + if (arcn->sb.st_atime == 0) { + arcn->sb.st_atime = arcn->sb.st_mtime; + arcn->sb.st_atimensec = arcn->sb.st_mtimensec; + } /* * If we can find the ascii names for gname and uname in the password @@ -1191,6 +1201,40 @@ expandname(char *buf, size_t len, char **gnu_name, const char *name, /* longest record we'll accept */ #define MAXXHDRSZ BLKMULT +static int +rd_time(struct timespec *ts, const char *keyword, char *p) +{ + const char *errstr; + char *q; + int multiplier; + + if ((q = strchr(p, '.')) != NULL) + *q = '\0'; + + ts->tv_sec = strtonum(p, 0, MAX_TIME_T, &errstr); + if (errstr != NULL) { + paxwarn(1, "%s is %s: %s", keyword, errstr, p); + return -1; + } + + ts->tv_nsec = 0; + + if (q == NULL) + return 0; + + multiplier = 100000000; + for (q++; *q != '\0'; q++) { + if (!isdigit((unsigned char)*q)) { + paxwarn(1, "%s contains non-digit", keyword); + return -1; + } + ts->tv_nsec += (*q - '0') * multiplier; + multiplier /= 10; + } + + return 0; +} + static int rd_xheader(ARCHD *arcn, int global, off_t size) { @@ -1269,6 +1313,18 @@ rd_xheader(ARCHD *arcn, int global, off_t size) } else if (!strcmp(keyword, "linkpath")) { arcn->ln_nlen = strlcpy(arcn->ln_name, p, sizeof(arcn->ln_name)); + } else if (!strcmp(keyword, "mtime")) { + ret = rd_time(&arcn->sb.st_mtim, keyword, p); + if (ret < 0) + break; + } else if (!strcmp(keyword, "atime")) { + ret = rd_time(&arcn->sb.st_atim, keyword, p); + if (ret < 0) + break; + } else if (!strcmp(keyword, "ctime")) { + ret = rd_time(&arcn->sb.st_ctim, keyword, p); + if (ret < 0) + break; } } p = nextp; -- 2.20.1