<pre style='margin:0'>
Christopher Nielsen (mascguy) pushed a commit to branch master
in repository macports-legacy-support.

</pre>
<p><a href="https://github.com/macports/macports-legacy-support/commit/1efc560b240a7a236f25e589d3afc306e02e8272">https://github.com/macports/macports-legacy-support/commit/1efc560b240a7a236f25e589d3afc306e02e8272</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 1efc560b240a7a236f25e589d3afc306e02e8272
</span>Author: Fred Wright <fw@fwright.net>
AuthorDate: Mon Jul 7 14:15:02 2025 -0700

<span style='display:block; white-space:pre;color:#404040;'>    test_utimens: New test, replacing old test_utimensat.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    This is a completely rewritten test, not relying on an Apple test
</span><span style='display:block; white-space:pre;color:#404040;'>    written for an unpublished test framework and then kludged.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    It also accommodates the correct reason for the nanoseconds mismatch
</span><span style='display:block; white-space:pre;color:#404040;'>    on non-APFS filesystems.
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    It also adds formerly missing tests for futimens().
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    TESTED:
</span><span style='display:block; white-space:pre;color:#404040;'>    Passes on all platforms.
</span>---
 test/test_utimens.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 301 insertions(+)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/test_utimens.c b/test/test_utimens.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..b033d72
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/test/test_utimens.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,301 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Copyright (c) 2025 Frederick H. G. Wright II <fw@fwright.net>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Permission to use, copy, modify, and distribute this software for any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * purpose with or without fee is hereby granted, provided that the above
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * copyright notice and this permission notice appear in all copies.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * This provides some tests of the futimens() and utimensat() functions.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <errno.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <fcntl.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <libgen.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdint.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <stdio.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <string.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <unistd.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/mount.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/param.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/stat.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#ifndef TEST_TEMP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#define TEST_TEMP "/dev/null"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct timespec timespec_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+typedef struct timeval timeval_t;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* Test cases copied from the old test */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static const timespec_t tptr[][2] = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0x12345678, 987654321 }, { 0x15263748, 123456789 }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_NOW }, { 0x15263748, 123456789 }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0x12345678, 987654321 }, { 0, UTIME_NOW }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_NOW }, { 0, UTIME_NOW }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_OMIT }, { 0x15263748, 123456789 }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0x12345678, 987654321 }, { 0, UTIME_OMIT }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_OMIT }, { 0, UTIME_OMIT }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_NOW }, { 0, UTIME_OMIT }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   { { 0, UTIME_OMIT }, { 0, UTIME_NOW }, },
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+create_file(const char *name, int verbose, int *fdp)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  int fd;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if ((fd = open(name, O_CREAT | O_RDWR,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("  *** creating '%s' failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           name, strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (verbose) printf("    created '%s'\n", name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (fdp) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    *fdp = fd;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (close(fd)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("  *** error closing '%s': %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           name, strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    return 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static void
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+print_case(const timespec_t *tts, const timespec_t *ots, const timespec_t *nts)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  printf("      {%ld, %ld}: {%ld, %ld} -> {%ld, %ld}\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         tts->tv_sec, tts->tv_nsec,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         ots->tv_sec, ots->tv_nsec, nts->tv_sec, nts->tv_nsec);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+do_tests(int mode, const char *path, int apfs,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         int verbose, int keepgoing, int *filter)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  int ret = 0, i, lret, fd = -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  struct stat pre_st, post_st;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  timespec_t now;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (verbose) printf("  Testing %s\n", mode ? "futimens()" : "utimensat()");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  ret = create_file(path, verbose, mode ? &fd : NULL);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (ret) return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  do {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    for (i = 0; i < sizeof(tptr)/sizeof(tptr[0]); i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (verbose) printf("    === {%ld, %ld} {%ld, %ld} ===\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          tptr[i][0].tv_sec, tptr[i][0].tv_nsec,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                          tptr[i][1].tv_sec, tptr[i][1].tv_nsec);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (clock_gettime(CLOCK_REALTIME, &now)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        printf("      *** clock_gettime() failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+               strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (mode ? fstat(fd, &pre_st) : stat(path, &pre_st)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        printf("      *** first stat() failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+               strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (mode ? futimens(fd, tptr[i])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          : utimensat(AT_FDCWD, path, tptr[i], 0)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        printf("      *** %s() failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+               mode ? "futimens" : "utimensat", strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (mode ? fstat(fd, &post_st) : stat(path, &post_st)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        printf("      *** second stat() failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+               strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      /* First do the atimes */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      lret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (tptr[i][0].tv_nsec == UTIME_NOW) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_atimespec.tv_sec < now.tv_sec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          printf("      *** post-stat atime seconds < now\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      } else if (tptr[i][0].tv_nsec == UTIME_OMIT) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_atimespec.tv_sec != pre_st.st_atimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            || post_st.st_atimespec.tv_nsec != pre_st.st_atimespec.tv_nsec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          printf("      *** atime inappropriately changed\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* Seconds must always match. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_atimespec.tv_sec != tptr[i][0].tv_sec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          if (!apfs && post_st.st_atimespec.tv_sec == pre_st.st_atimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              && post_st.st_atimespec.tv_nsec == pre_st.st_atimespec.tv_nsec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              && tptr[i][1].tv_nsec == UTIME_OMIT) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * A bug (non-APFS only, including in Apple's code) fails to set
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * the atime when the mtime operand is set to UTIME_OMIT.  We check
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             * for that case here, and tolerate the error.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (!*filter || verbose) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              printf("      *** "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                     "tolerating known bug setting atime without mtime.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ++*filter;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            printf("      *** new atime seconds mismatched intended value.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          /*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           * Nanoseconds must match on APFS, but other filesystems may not
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           * support subseconds at all, so we allow it if before and after
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           * were both zero.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          if (post_st.st_atimespec.tv_nsec != tptr[i][0].tv_nsec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (!apfs
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && pre_st.st_atimespec.tv_nsec == 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && post_st.st_atimespec.tv_nsec == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              if (verbose > 1) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                printf("      *** allowing non-APFS zero atime nanos.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              printf("      *** "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                     "new atime nanoseconds mismatched intended value.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (lret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        print_case(&tptr[i][0], &pre_st.st_atimespec, &post_st.st_atimespec);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      ret |= lret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (ret && !keepgoing) break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      /* Now do the mtimes */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      lret = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (tptr[i][1].tv_nsec == UTIME_NOW) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_mtimespec.tv_sec < now.tv_sec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          printf("      *** post-stat mtime seconds < now\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      } else if (tptr[i][1].tv_nsec == UTIME_OMIT) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_mtimespec.tv_sec != pre_st.st_mtimespec.tv_sec
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            || post_st.st_mtimespec.tv_nsec != pre_st.st_mtimespec.tv_nsec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          printf("      *** mtime inappropriately changed\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        /* Seconds must always match. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if (post_st.st_mtimespec.tv_sec != tptr[i][1].tv_sec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          printf("      *** new mtime seconds mismatched intended value.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          /* See above regarding nanoseconds matching */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          if (post_st.st_mtimespec.tv_nsec != tptr[i][1].tv_nsec) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            if (!apfs
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && pre_st.st_mtimespec.tv_nsec == 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                && post_st.st_mtimespec.tv_nsec == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              if (verbose > 1) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                printf("      *** allowing non-APFS zero mtime nanos.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              printf("      *** "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                     "new mtime nanoseconds mismatched intended value.\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+              lret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (lret) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        print_case(&tptr[i][1], &pre_st.st_mtimespec, &post_st.st_mtimespec);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      ret |= lret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (ret && !keepgoing) break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           if (ret && !keepgoing) break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  } while (0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (fd >= 0 && close(fd)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("  *** error closing '%s': %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           path, strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  /* If stopping on error, leave the file for perusal, else delete it */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (!ret || keepgoing) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (unlink(path)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      printf("    *** error deleting '%s': %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+             path, strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      if (verbose) printf("    deleted '%s'\n", path);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+main(int argc, char *argv[])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  int argn = 1, verbose = 0, keepgoing = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  int ret = 0, apfs = 0, filter = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  char *progname = basename(argv[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  pid_t pid = getpid();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  const char *cp;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  char chr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  struct statfs sfs = { 0 };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  char tpath[MAXPATHLEN];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  while (argn < argc && argv[argn][0] == '-') {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    cp = argv[argn];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    while ((chr = *++cp)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      switch (chr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        case 'K': ++keepgoing; break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        case 'v': ++verbose; break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ++argn;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  (void) snprintf(tpath, sizeof(tpath), TEST_TEMP "/%s-%u", progname, pid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (verbose) printf("%s starting.\n", progname);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  if (statfs(TEST_TEMP, &sfs)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    printf("  *** statfs() for '" TEST_TEMP "' failed: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           strerror(errno), errno);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ret = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (verbose) printf("  filesystem type is '%s'\n", sfs.f_fstypename);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    apfs = !strcmp(sfs.f_fstypename, "apfs");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ret = do_tests(0, tpath, apfs, verbose, keepgoing, &filter);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    if (!ret || keepgoing) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+      ret = do_tests(1, tpath, apfs, verbose, keepgoing, &filter);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  printf("%s %s.\n", progname, ret ? "failed" : "passed");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+  return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span></pre><pre style='margin:0'>

</pre>