2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021 The FreeBSD Foundation
7 * This software was developed by Ka Ho Ng under sponsorship from
8 * the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/param.h>
33 #include <sys/mount.h>
40 static off_t file_max_blocks = 32;
41 static const char byte_to_fill = 0x5f;
44 fill(int fd, off_t offset, off_t len)
52 if (fstat(fd, &statbuf) == -1)
54 blocksize = statbuf.st_blksize;
56 buf = malloc(blocksize);
61 blen = len < (off_t)blocksize ? len : blocksize;
62 memset(buf, byte_to_fill, blen);
63 if (pwrite(fd, buf, blen, offset) != (ssize_t)blen) {
78 struct statfs statfsbuf;
80 if (statfs(".", &statfsbuf) == -1)
82 return statfsbuf.f_iosize;
86 check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t file_sz)
95 blocksize = fd_get_blksize();
99 buf = malloc(blocksize * 2);
102 sblk = buf + blocksize;
104 memset(sblk, 0, blocksize);
106 if ((uint64_t)hole_start + hole_len > (uint64_t)file_sz)
107 hole_len = file_sz - hole_start;
110 * Check hole is zeroed.
115 blen = resid < (off_t)blocksize ? resid : blocksize;
116 if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
120 if (memcmp(buf, sblk, blen) != 0) {
128 memset(sblk, byte_to_fill, blocksize);
131 * Check file region before hole is zeroed.
136 blen = resid < (off_t)blocksize ? resid : blocksize;
137 if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
141 if (memcmp(buf, sblk, blen) != 0) {
150 * Check file region after hole is zeroed.
152 offset = hole_start + hole_len;
153 resid = file_sz - offset;
155 blen = resid < (off_t)blocksize ? resid : blocksize;
156 if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
160 if (memcmp(buf, sblk, blen) != 0) {
169 * Check file size matches with expected file size.
171 if (fstat(fd, &statbuf) == -1)
173 if (statbuf.st_size != file_sz)
181 * Check aligned deallocation
183 ATF_TC_WITHOUT_HEAD(aligned_dealloc);
184 ATF_TC_BODY(aligned_dealloc, tc)
186 struct spacectl_range range;
187 off_t offset, length;
191 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
192 range.r_offset = offset = blocksize;
193 range.r_len = length = (file_max_blocks - 1) * blocksize -
196 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
197 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
198 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
199 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
200 ATF_CHECK(check_content_dealloc(fd, offset, length,
201 file_max_blocks * blocksize) == 0);
202 ATF_REQUIRE(close(fd) == 0);
206 * Check unaligned deallocation
208 ATF_TC_WITHOUT_HEAD(unaligned_dealloc);
209 ATF_TC_BODY(unaligned_dealloc, tc)
211 struct spacectl_range range;
212 off_t offset, length;
216 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
217 range.r_offset = offset = blocksize / 2;
218 range.r_len = length = (file_max_blocks - 1) * blocksize +
219 blocksize / 2 - offset;
221 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
222 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
223 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
224 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
225 ATF_CHECK(check_content_dealloc(fd, offset, length,
226 file_max_blocks * blocksize) == 0);
227 ATF_REQUIRE(close(fd) == 0);
231 * Check aligned deallocation from certain offset to OFF_MAX
233 ATF_TC_WITHOUT_HEAD(aligned_dealloc_offmax);
234 ATF_TC_BODY(aligned_dealloc_offmax, tc)
236 struct spacectl_range range;
237 off_t offset, length;
241 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
242 range.r_offset = offset = blocksize;
243 range.r_len = length = OFF_MAX - offset;
245 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
246 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
247 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
248 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
249 ATF_CHECK(check_content_dealloc(fd, offset, length,
250 file_max_blocks * blocksize) == 0);
251 ATF_REQUIRE(close(fd) == 0);
255 * Check unaligned deallocation from certain offset to OFF_MAX
257 ATF_TC_WITHOUT_HEAD(unaligned_dealloc_offmax);
258 ATF_TC_BODY(unaligned_dealloc_offmax, tc)
260 struct spacectl_range range;
261 off_t offset, length;
265 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
266 range.r_offset = offset = blocksize / 2;
267 range.r_len = length = OFF_MAX - offset;
269 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
270 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
271 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
272 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
273 ATF_CHECK(check_content_dealloc(fd, offset, length,
274 file_max_blocks * blocksize) == 0);
275 ATF_REQUIRE(close(fd) == 0);
279 * Check aligned deallocation around EOF
281 ATF_TC_WITHOUT_HEAD(aligned_dealloc_eof);
282 ATF_TC_BODY(aligned_dealloc_eof, tc)
284 struct spacectl_range range;
285 off_t offset, length;
289 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
290 range.r_offset = offset = blocksize;
291 range.r_len = length = (file_max_blocks + 1) * blocksize -
294 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
295 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
296 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
297 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
298 ATF_CHECK(check_content_dealloc(fd, offset, length,
299 file_max_blocks * blocksize) == 0);
300 ATF_REQUIRE(close(fd) == 0);
304 * Check unaligned deallocation around EOF
306 ATF_TC_WITHOUT_HEAD(unaligned_dealloc_eof);
307 ATF_TC_BODY(unaligned_dealloc_eof, tc)
309 struct spacectl_range range;
310 off_t offset, length;
314 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
315 range.r_offset = offset = blocksize / 2;
316 range.r_len = length = file_max_blocks * blocksize + blocksize / 2 -
319 ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
320 O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
321 ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
322 ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0);
323 ATF_CHECK(check_content_dealloc(fd, offset, length,
324 file_max_blocks * blocksize) == 0);
325 ATF_REQUIRE(close(fd) == 0);
330 ATF_TP_ADD_TC(tp, aligned_dealloc);
331 ATF_TP_ADD_TC(tp, unaligned_dealloc);
332 ATF_TP_ADD_TC(tp, aligned_dealloc_eof);
333 ATF_TP_ADD_TC(tp, unaligned_dealloc_eof);
334 ATF_TP_ADD_TC(tp, aligned_dealloc_offmax);
335 ATF_TP_ADD_TC(tp, unaligned_dealloc_offmax);
337 return atf_no_error();