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'; } /*