<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>