summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorPhilip Guenther <guenther@cvs.openbsd.org>2024-05-18 05:21:39 +0000
committerPhilip Guenther <guenther@cvs.openbsd.org>2024-05-18 05:21:39 +0000
commita4f1f7bd07f70c4a60cf332adcba0c5b0c2fde51 (patch)
treeb531491c1ef3134fbca5d6a28b3ce63acd4bcbb3 /bin
parent2b9cf588c859297ca314458a32fb3b90ac6b3ccc (diff)
When comparing mtimes for the -u and -Z options and the target is
'too old', use pathconfat(_PC_TIMESTAMP_RESOLUTION, AT_SYMLINK_NOFOLLOW) to get the timestamp resolution to which the _source_ timestamp should be truncated for a stable comparison. Problem reported by Walter Alejandro Iglesias (wai(at)roquesor.com) ok millert@
Diffstat (limited to 'bin')
-rw-r--r--bin/pax/ar_subs.c75
1 files changed, 55 insertions, 20 deletions
diff --git a/bin/pax/ar_subs.c b/bin/pax/ar_subs.c
index c542aa61d57..172d6847b3a 100644
--- a/bin/pax/ar_subs.c
+++ b/bin/pax/ar_subs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ar_subs.c,v 1.51 2023/07/10 16:28:33 jeremy Exp $ */
+/* $OpenBSD: ar_subs.c,v 1.52 2024/05/18 05:21:38 guenther Exp $ */
/* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */
/*-
@@ -146,23 +146,60 @@ list(void)
}
static int
-cmp_file_times(int mtime_flag, int ctime_flag, ARCHD *arcn, struct stat *sbp)
+cmp_file_times(int mtime_flag, int ctime_flag, ARCHD *arcn, const char *path)
{
struct stat sb;
+ long res;
- if (sbp == NULL) {
- if (lstat(arcn->name, &sb) != 0)
- return (0);
- sbp = &sb;
- }
+ if (path == NULL)
+ path = arcn->name;
+ if (lstat(path, &sb) != 0)
+ return (0);
- if (ctime_flag && mtime_flag)
- return (timespeccmp(&arcn->sb.st_mtim, &sbp->st_mtim, <=) &&
- timespeccmp(&arcn->sb.st_ctim, &sbp->st_ctim, <=));
- else if (ctime_flag)
- return (timespeccmp(&arcn->sb.st_ctim, &sbp->st_ctim, <=));
- else
- return (timespeccmp(&arcn->sb.st_mtim, &sbp->st_mtim, <=));
+ /*
+ * The target (sb) mtime might be rounded down due to the limitations
+ * of the FS it's on. If it's strictly greater or we don't care about
+ * mtime, then precision doesn't matter, so check those cases first.
+ */
+ if (ctime_flag && mtime_flag) {
+ if (timespeccmp(&arcn->sb.st_mtim, &sb.st_mtim, <=))
+ return timespeccmp(&arcn->sb.st_ctim, &sb.st_ctim, <=);
+ if (!timespeccmp(&arcn->sb.st_ctim, &sb.st_ctim, <=))
+ return 0;
+ /* <= ctim, but >= mtim */
+ } else if (ctime_flag)
+ return timespeccmp(&arcn->sb.st_ctim, &sb.st_ctim, <=);
+ else if (timespeccmp(&arcn->sb.st_mtim, &sb.st_mtim, <=))
+ return 1;
+
+ /*
+ * If we got here then the target arcn > sb for mtime *and* that's
+ * the deciding factor. Check whether they're equal after rounding
+ * down the arcn mtime to the precision of the target path.
+ */
+ res = pathconfat(AT_FDCWD, path, _PC_TIMESTAMP_RESOLUTION,
+ AT_SYMLINK_NOFOLLOW);
+ if (res == -1)
+ return 0;
+
+ /* nanosecond resolution? previous comparisons were accurate */
+ if (res == 1)
+ return 0;
+
+ /* common case: second accuracy */
+ if (res == 1000000000)
+ return arcn->sb.st_mtime <= sb.st_mtime;
+
+ if (res < 1000000000) {
+ struct timespec ts = arcn->sb.st_mtim;
+ ts.tv_nsec = (ts.tv_nsec / res) * res;
+ return timespeccmp(&ts, &sb.st_mtim, <=);
+ } else {
+ /* not a POSIX compliant FS */
+ res /= 1000000000;
+ return ((arcn->sb.st_mtime / res) * res) <= sb.st_mtime;
+ return arcn->sb.st_mtime <= ((sb.st_mtime / res) * res);
+ }
}
/*
@@ -842,14 +879,12 @@ copy(void)
/*
* if existing file is same age or newer skip
*/
- res = lstat(dirbuf, &sb);
- *dest_pt = '\0';
-
- if (res == 0) {
+ if (cmp_file_times(uflag, Dflag, arcn, dirbuf)) {
+ *dest_pt = '\0';
ftree_skipped_newer(arcn);
- if (cmp_file_times(uflag, Dflag, arcn, &sb))
- continue;
+ continue;
}
+ *dest_pt = '\0';
}
/*