summaryrefslogtreecommitdiff
path: root/bin/pax/tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/pax/tar.c')
-rw-r--r--bin/pax/tar.c68
1 files changed, 64 insertions, 4 deletions
diff --git a/bin/pax/tar.c b/bin/pax/tar.c
index a41a6d4d0a0..949e9ce4ec1 100644
--- a/bin/pax/tar.c
+++ b/bin/pax/tar.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tar.c,v 1.77 2023/12/22 20:32:29 jca Exp $ */
+/* $OpenBSD: tar.c,v 1.78 2023/12/27 08:29:41 jca Exp $ */
/* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */
/*-
@@ -978,6 +978,39 @@ xheader_add_ull(struct xheader *xhdr, const char *keyword,
return 0;
}
+static int
+xheader_add_ts(struct xheader *xhdr, const char *keyword,
+ const struct timespec *value)
+{
+ struct xheader_record *rec;
+ int reclen, tmplen;
+ char *s;
+
+ tmplen = MINXHDRSZ;
+ do {
+ reclen = tmplen;
+ tmplen = snprintf(NULL, 0, "%d %s=%lld.%09ld\n", reclen,
+ keyword, (long long)value->tv_sec, (long)value->tv_nsec);
+ } while (tmplen >= 0 && tmplen != reclen);
+ if (tmplen < 0)
+ return -1;
+
+ rec = calloc(1, sizeof(*rec));
+ if (rec == NULL)
+ return -1;
+ rec->reclen = reclen;
+ if (asprintf(&s, "%d %s=%lld.%09ld\n", reclen, keyword,
+ (long long)value->tv_sec, (long)value->tv_nsec) < 0) {
+ free(rec);
+ return -1;
+ }
+ rec->record = s;
+
+ SLIST_INSERT_HEAD(xhdr, rec, entry);
+
+ return 0;
+}
+
static void
xheader_free(struct xheader *xhdr)
{
@@ -1060,6 +1093,7 @@ wr_ustar_or_pax(ARCHD *arcn, int ustar)
#ifndef SMALL
struct xheader xhdr = SLIST_HEAD_INITIALIZER(xhdr);
#endif
+ int bad_mtime;
/*
* check for those file system types ustar cannot store
@@ -1249,9 +1283,35 @@ wr_ustar_or_pax(ARCHD *arcn, int ustar)
if (ul_oct(gid_nobody, hd->gid, sizeof(hd->gid), 3))
goto out;
}
- if (ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime, hd->mtime,
- sizeof(hd->mtime), 3) ||
- ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3))
+ bad_mtime = ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime,
+ hd->mtime, sizeof(hd->mtime), 3);
+ if (bad_mtime && ustar)
+ goto out;
+#ifndef SMALL
+ if (!ustar) {
+ /*
+ * The pax format can preserve atime and store
+ * a possibly more accurate mtime.
+ *
+ * ctime isn't specified by POSIX so omit it.
+ */
+ if (xheader_add_ts(&xhdr, "atime", &arcn->sb.st_atim) == -1) {
+ paxwarn(1, "Couldn't preserve %s in pax format for %s",
+ "atime", arcn->org_name);
+ xheader_free(&xhdr);
+ return (1);
+ }
+ if ((bad_mtime || arcn->sb.st_mtime < 0 ||
+ arcn->sb.st_mtim.tv_nsec != 0) &&
+ xheader_add_ts(&xhdr, "mtime", &arcn->sb.st_mtim) == -1) {
+ paxwarn(1, "Couldn't preserve %s in pax format for %s",
+ "mtime", arcn->org_name);
+ xheader_free(&xhdr);
+ return (1);
+ }
+ }
+#endif
+ if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3))
goto out;
if (!Nflag) {
if ((name = user_from_uid(arcn->sb.st_uid, 1)) != NULL)