git: 8b418c83d175 - main - cp: Adjust the sparse file tests.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 08 Feb 2023 16:50:06 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=8b418c83d175fde3b1f65210509ddcf2ac248d9f

commit 8b418c83d175fde3b1f65210509ddcf2ac248d9f
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-02-08 16:49:29 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-02-08 16:49:50 +0000

    cp: Adjust the sparse file tests.
    
    * The sparsity check was ineffective: it compared the apparent size in bytes to the actual size in blocks.  Instead, write a tool that reliably detects sparseness.
    * Some of the seq commands were missing an argument.
    * Based on empirical evidence, 1 MB holes are not necessarily large enough to be preserved by the underlying filesystem.  Increase the hole size to 16 MB.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    cracauer
    Differential Revision:  https://reviews.freebsd.org/D38414
---
 bin/cp/tests/Makefile   |  2 ++
 bin/cp/tests/cp_test.sh | 46 +++++++++++++++----------------
 bin/cp/tests/sparse.c   | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 23 deletions(-)

diff --git a/bin/cp/tests/Makefile b/bin/cp/tests/Makefile
index faad22df713a..1e480ad706d1 100644
--- a/bin/cp/tests/Makefile
+++ b/bin/cp/tests/Makefile
@@ -3,5 +3,7 @@
 PACKAGE=	tests
 
 ATF_TESTS_SH=	cp_test
+PROGS+=		sparse
+BINDIR=		${TESTSDIR}
 
 .include <bsd.test.mk>
diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh
index 932bd2221ba3..559a5ecc3c00 100755
--- a/bin/cp/tests/cp_test.sh
+++ b/bin/cp/tests/cp_test.sh
@@ -201,7 +201,7 @@ recursive_link_Lflag_body()
 
 file_is_sparse()
 {
-	atf_check test "$(stat -f "%b" "$1")" != "$(stat -f "%z" "$1")"
+	atf_check ${0%/*}/sparse "$1"
 }
 
 files_are_equal()
@@ -213,8 +213,8 @@ files_are_equal()
 atf_test_case sparse_leading_hole
 sparse_leading_hole_body()
 {
-	# A one-megabyte hole followed by one megabyte of data
-	truncate -s 1M foo
+	# A 16-megabyte hole followed by one megabyte of data
+	truncate -s 16M foo
 	seq -f%015g 65536 >>foo
 	file_is_sparse foo
 
@@ -227,14 +227,14 @@ atf_test_case sparse_multiple_holes
 sparse_multiple_holes_body()
 {
 	# Three one-megabyte blocks of data preceded, separated, and
-	# followed by one-megabyte holes
-	truncate -s 1M foo
-	seq -f%015g >>foo
-	truncate -s 3M foo
-	seq -f%015g >>foo
-	truncate -s 5M foo
-	seq -f%015g >>foo
-	truncate -s 7M foo
+	# followed by 16-megabyte holes
+	truncate -s 16M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 33M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 50M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 67M foo
 	file_is_sparse foo
 
 	atf_check cp foo bar
@@ -245,8 +245,8 @@ sparse_multiple_holes_body()
 atf_test_case sparse_only_hole
 sparse_only_hole_body()
 {
-	# A one-megabyte hole
-	truncate -s 1M foo
+	# A 16-megabyte hole
+	truncate -s 16M foo
 	file_is_sparse foo
 
 	atf_check cp foo bar
@@ -258,14 +258,14 @@ atf_test_case sparse_to_dev
 sparse_to_dev_body()
 {
 	# Three one-megabyte blocks of data preceded, separated, and
-	# followed by one-megabyte holes
-	truncate -s 1M foo
-	seq -f%015g >>foo
-	truncate -s 3M foo
-	seq -f%015g >>foo
-	truncate -s 5M foo
-	seq -f%015g >>foo
-	truncate -s 7M foo
+	# followed by 16-megabyte holes
+	truncate -s 16M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 33M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 50M foo
+	seq -f%015g 65536 >>foo
+	truncate -s 67M foo
 	file_is_sparse foo
 
 	atf_check -o file:foo cp foo /dev/stdout
@@ -274,9 +274,9 @@ sparse_to_dev_body()
 atf_test_case sparse_trailing_hole
 sparse_trailing_hole_body()
 {
-	# One megabyte of data followed by a one-megabyte hole
+	# One megabyte of data followed by a 16-megabyte hole
 	seq -f%015g 65536 >foo
-	truncate -s 2M foo
+	truncate -s 17M foo
 	file_is_sparse foo
 
 	atf_check cp foo bar
diff --git a/bin/cp/tests/sparse.c b/bin/cp/tests/sparse.c
new file mode 100644
index 000000000000..78957581a56c
--- /dev/null
+++ b/bin/cp/tests/sparse.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static bool verbose;
+
+/*
+ * Returns true if the file named by its argument is sparse, i.e. if
+ * seeking to SEEK_HOLE returns a different value than seeking to
+ * SEEK_END.
+ */
+static bool
+sparse(const char *filename)
+{
+	off_t hole, end;
+	int fd;
+
+	if ((fd = open(filename, O_RDONLY)) < 0 ||
+	    (hole = lseek(fd, 0, SEEK_HOLE)) < 0 ||
+	    (end = lseek(fd, 0, SEEK_END)) < 0)
+		err(1, "%s", filename);
+	close(fd);
+	if (end > hole) {
+		if (verbose)
+			printf("%s: hole at %zu\n", filename, (size_t)hole);
+		return (true);
+	}
+	return (false);
+}
+
+static void
+usage(void)
+{
+
+	fprintf(stderr, "usage: sparse [-v] file [...]\n");
+	exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int opt, rv;
+
+	while ((opt = getopt(argc, argv, "v")) != -1) {
+		switch (opt) {
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			usage();
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+	if (argc == 0)
+		usage();
+	rv = EXIT_SUCCESS;
+	while (argc-- > 0)
+		if (!sparse(*argv++))
+			rv = EXIT_FAILURE;
+	exit(rv);
+}