<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/77e5cc22a46451968e2c14d88d85494a5dad8f24">https://github.com/macports/macports-legacy-support/commit/77e5cc22a46451968e2c14d88d85494a5dad8f24</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 77e5cc22a46451968e2c14d88d85494a5dad8f24
</span>Author: Fred Wright <fw@fwright.net>
AuthorDate: Fri May 30 11:47:47 2025 -0700
<span style='display:block; white-space:pre;color:#404040;'> Add clonefile test.
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> This only tests the expected failing case on non-APFS volumes, which
</span><span style='display:block; white-space:pre;color:#404040;'> is the only possibility in the legacy-support implementation. The
</span><span style='display:block; white-space:pre;color:#404040;'> test is skipped when running on APFS.
</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 or is skipped on all platforms.
</span>---
test/test_clonefile.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 158 insertions(+)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/test/test_clonefile.c b/test/test_clonefile.c
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..7593518
</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_clonefile.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,158 @@
</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 a limited test of *clonefile*(), mainly to test the disallowed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * cases on HFS+, since native clonefile() support is available on all OS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * versions that support APFS.
</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 <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/attr.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/clonefile.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <sys/fcntl.h>
</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;'>+
</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;'>+/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * Determine filesystem cloning support for given file.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ * -1 error, 0 no cloning allowed, 1 cloning allowed
</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;'>+get_cloneable(const char *path, int verbose)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ struct statfs sfb;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ struct attrlist al = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .bitmapcount = ATTR_BIT_MAP_COUNT,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ uint32_t abuf[1024] = {0};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ vol_capabilities_attr_t *vc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (statfs(path, &sfb)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" statfs() for '%s' failed: %s\n", path, strerror(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(" %s is in volume %s\n", path, sfb.f_mntonname);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (getattrlist(sfb.f_mntonname, &al, abuf, sizeof(abuf), 0)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fprintf(stderr, " getattrlist() for '%s' failed: %s\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sfb.f_mntonname, strerror(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 (abuf[0] != sizeof(*vc) + sizeof(abuf[0])) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" getattrlist() for '%s' returned wrong size: %d\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sfb.f_mntonname, abuf[0]);
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ vc = (vol_capabilities_attr_t *) &abuf[1];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (vc->valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_CLONE) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (vc->capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_CLONE) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose) printf(" VOL_CAP_INT_CLONE is set\n");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return 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(" VOL_CAP_INT_CLONE is not set\n");
</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;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose) printf(" VOL_CAP_INT_CLONE is unspecified\n");
</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;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+static int
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+check_status(int status, const char *call, int verbose)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (status != -1) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s unexpectedly succeeded\n", call);
</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 (errno != ENOTSUP) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s returned incorrect errno: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ call, 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) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s returned expected errno: %s (%d)\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ call, strerror(errno), errno);
</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;'>+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 verbose = 0, ret = 0, cloneable, fd;
</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;'>+ char dest[MAXPATHLEN];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (argc > 1 && !strcmp(argv[1], "-v")) verbose = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (void) snprintf(dest, sizeof(dest), "%s/%s-%u", TEST_TEMP, progname, pid);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (verbose) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf("%s starting.\n", progname);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" %s -> %s\n", argv[0], dest);
</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;'>+ cloneable = get_cloneable(argv[0], verbose);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (cloneable < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf("%s failed.\n", progname);
</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 (cloneable) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf("%s skipped due to cloneable volume.\n", progname);
</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;'>+ ret |= check_status(
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ clonefile(argv[0], dest, 0),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "clonefile(<src>, <dst>, 0)",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ verbose
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ret |= check_status(
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ clonefileat(AT_FDCWD, argv[0], AT_FDCWD, dest, 0),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "clonefileat(AT_FDCWD, <src>, AT_FDCWD, <dst>, 0)",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ verbose
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((fd = open(argv[0], O_RDONLY)) < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ printf(" open() for %s failed: %s\n", argv[0], strerror(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;'>+ ret |= check_status(
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fclonefileat(fd, AT_FDCWD, dest, 0),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "fclonefileat(<srcfd>, AT_FDCWD, <dst>, 0)",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ verbose
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (void) close(fd);
</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>