truncate: fix a minor nit + add a hole-punching test

The struct spacectl_range we use is only really used in these three
lines of code, so re-scope it down to just the dealloc branch.  This is
marginally easier to reason about what might be necessary to replace in
porting our truncate(1) to other platforms.

While we're here, add a test for the -d flag to be sure it really does
punch a hole in the file.  The test also tries to confirm that it does
not disturb other segments of the file in the process, just to inspire
some confidence that it's not corrupting the file somehow.

Sponsored by:	Klara, Inc.
Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D51207
This commit is contained in:
Kyle Evans
2026-01-21 17:34:27 -06:00
parent 6c61f58562
commit eacc501eff
2 changed files with 50 additions and 1 deletions
+48
View File
@@ -438,6 +438,53 @@ rounddown_zero_body()
[ ${st_size} -eq 0 ] || atf_fail "new file should now be 0 bytes"
}
atf_test_case deallocate
deallocate_head()
{
atf_set "descr" "Verifies that -d punches a hole in the file"
atf_set "require.user" "root"
}
deallocate_body()
{
blocksz=$(stat -h . | cut -f1 -d' ')
atf_check test -n "$blocksz"
# We use /dev/random here to defeat ZFS compression, which would
# collapse ranges of zeroes into holes without us deallocating it. This
# isn't a concern below because those are specificially for creating our
# reference files -- we expect the deallocate operation to result in
# ranges of zeroes, whether they end up creating a hole or not.
filesz=$((blocksz * 3))
atf_check -e not-empty dd if=/dev/random of=sparse \
bs=${filesz} count=1 conv=notrunc
atf_check cp sparse sparse.orig
atf_check cp sparse sparse.orig.orig
# Punch a hole in the middle, ensure that bit is zeroed out.
atf_check -e not-empty dd if=/dev/zero of=sparse.orig \
bs=${blocksz} oseek=1 count=1 conv=notrunc
atf_check truncate -d -o ${blocksz} -l ${blocksz} sparse
atf_check cmp -s sparse sparse.orig
# Clobber the end part of the original file and punch a hole in
# the same spot on the new file, ensure that it has zeroed out that
# portion.
atf_check -e not-empty dd if=/dev/zero of=sparse.orig \
bs=${blocksz} oseek=2 count=1 conv=notrunc
atf_check truncate -d -o $((blocksz * 2)) -l ${blocksz} sparse
atf_check cmp -s sparse sparse.orig
# Now bring the original file back and make sure that punching a hole
# in data at the beginning doesn't disturb the data at the end.
atf_check cp sparse.orig.orig sparse.orig
atf_check cp sparse.orig.orig sparse
atf_check -e not-empty dd if=/dev/zero of=sparse.orig \
bs=${blocksz} oseek=0 count=1 conv=notrunc
atf_check truncate -d -l ${blocksz} sparse
atf_check cmp -s sparse sparse.orig
}
atf_init_test_cases()
{
atf_add_test_case illegal_option
@@ -459,4 +506,5 @@ atf_init_test_cases()
atf_add_test_case roundup
atf_add_test_case rounddown
atf_add_test_case rounddown_zero
atf_add_test_case deallocate
}
+2 -1
View File
@@ -62,7 +62,6 @@ main(int argc, char **argv)
int do_refer;
int got_size;
char *fname, *rname;
struct spacectl_range sr;
fd = -1;
rsize = tsize = sz = off = 0;
@@ -198,6 +197,8 @@ main(int argc, char **argv)
tsize = 0;
if (do_dealloc == 1) {
struct spacectl_range sr;
sr.r_offset = off;
sr.r_len = len;
r = fspacectl(fd, SPACECTL_DEALLOC, &sr, 0, &sr);