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
This commit is contained in:
Dag-Erling Smørgrav 2023-02-08 16:49:29 +00:00
parent 9df6eeabb3
commit 8b418c83d1
3 changed files with 98 additions and 23 deletions

View file

@ -3,5 +3,7 @@
PACKAGE= tests
ATF_TESTS_SH= cp_test
PROGS+= sparse
BINDIR= ${TESTSDIR}
.include <bsd.test.mk>

View file

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

73
bin/cp/tests/sparse.c Normal file
View file

@ -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);
}