On Wed May 15 10:20:04 2024 Philip Guenther wrote:
> I think you've managed to hit a spot where the POSIX standard doesn't
> provide a way for a program to find the information it needs to do its job
> correctly. I've filed a ticket there
> https://austingroupbugs.net/view.php?id=1831
>
> We'll see if my understanding of pathconf() is incorrect or if someone has
> a great idea for how to get around this...
>
>
> Philip Guenther
>
Hi Philip,
I get it working but I don't know if what I did is fine.
As I'd told you the problem was ctime (when using -Y), so I added one
conditional to your diff where it checks only mtime and it works:
Index: ar_subs.c
===================================================================
RCS file: /cvs/src/bin/pax/ar_subs.c,v
diff -u -p -r1.51 ar_subs.c
--- ar_subs.c 10 Jul 2023 16:28:33 -0000 1.51
+++ ar_subs.c 15 May 2024 08:19:08 -0000
@@ -146,23 +146,61 @@ 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);
+
+ /*
+ * 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 (mtime_flag) {
+ return timespeccmp(&arcn->sb.st_mtim, &sb.st_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 = pathconf(path, _PC_TIMESTAMP_RESOLUTION);
+ 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);
}
-
- 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, <=));
}
/*
@@ -842,14 +880,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';
}
/*