diff options
Diffstat (limited to 'tests/sys/file/fspacectl_test.c')
-rw-r--r-- | tests/sys/file/fspacectl_test.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/tests/sys/file/fspacectl_test.c b/tests/sys/file/fspacectl_test.c new file mode 100644 index 000000000000..714474b76b05 --- /dev/null +++ b/tests/sys/file/fspacectl_test.c @@ -0,0 +1,338 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Ka Ho Ng under sponsorship from + * the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <atf-c.h> +#include <fcntl.h> +#include <malloc.h> + +static off_t file_max_blocks = 32; +static const char byte_to_fill = 0x5f; + +static int +fill(int fd, off_t offset, off_t len) +{ + int error; + size_t blen; + char *buf; + struct stat statbuf; + blksize_t blocksize; + + if (fstat(fd, &statbuf) == -1) + return (1); + blocksize = statbuf.st_blksize; + error = 0; + buf = malloc(blocksize); + if (buf == NULL) + return (1); + + while (len > 0) { + blen = len < (off_t)blocksize ? len : blocksize; + memset(buf, byte_to_fill, blen); + if (pwrite(fd, buf, blen, offset) != (ssize_t)blen) { + error = 1; + break; + } + len -= blen; + offset += blen; + } + + free(buf); + return (error); +} + +static blksize_t +fd_get_blksize(void) +{ + struct statfs statfsbuf; + + if (statfs(".", &statfsbuf) == -1) + return (-1); + return statfsbuf.f_iosize; +} + +static int +check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t file_sz) +{ + int error; + size_t blen; + off_t offset, resid; + struct stat statbuf; + char *buf, *sblk; + blksize_t blocksize; + + blocksize = fd_get_blksize(); + if (blocksize == -1) + return (1); + error = 0; + buf = malloc(blocksize * 2); + if (buf == NULL) + return (1); + sblk = buf + blocksize; + + memset(sblk, 0, blocksize); + + if ((uint64_t)hole_start + hole_len > (uint64_t)file_sz) + hole_len = file_sz - hole_start; + + /* + * Check hole is zeroed. + */ + offset = hole_start; + resid = hole_len; + while (resid > 0) { + blen = resid < (off_t)blocksize ? resid : blocksize; + if (pread(fd, buf, blen, offset) != (ssize_t)blen) { + error = 1; + break; + } + if (memcmp(buf, sblk, blen) != 0) { + error = 1; + break; + } + resid -= blen; + offset += blen; + } + + memset(sblk, byte_to_fill, blocksize); + + /* + * Check file region before hole is zeroed. + */ + offset = 0; + resid = hole_start; + while (resid > 0) { + blen = resid < (off_t)blocksize ? resid : blocksize; + if (pread(fd, buf, blen, offset) != (ssize_t)blen) { + error = 1; + break; + } + if (memcmp(buf, sblk, blen) != 0) { + error = 1; + break; + } + resid -= blen; + offset += blen; + } + + /* + * Check file region after hole is zeroed. + */ + offset = hole_start + hole_len; + resid = file_sz - offset; + while (resid > 0) { + blen = resid < (off_t)blocksize ? resid : blocksize; + if (pread(fd, buf, blen, offset) != (ssize_t)blen) { + error = 1; + break; + } + if (memcmp(buf, sblk, blen) != 0) { + error = 1; + break; + } + resid -= blen; + offset += blen; + } + + /* + * Check file size matches with expected file size. + */ + if (fstat(fd, &statbuf) == -1) + error = -1; + if (statbuf.st_size != file_sz) + error = -1; + + free(buf); + return (error); +} + +/* + * Check aligned deallocation + */ +ATF_TC_WITHOUT_HEAD(aligned_dealloc); +ATF_TC_BODY(aligned_dealloc, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize; + range.r_len = length = (file_max_blocks - 1) * blocksize - + range.r_offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +/* + * Check unaligned deallocation + */ +ATF_TC_WITHOUT_HEAD(unaligned_dealloc); +ATF_TC_BODY(unaligned_dealloc, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize / 2; + range.r_len = length = (file_max_blocks - 1) * blocksize + + blocksize / 2 - offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +/* + * Check aligned deallocation from certain offset to OFF_MAX + */ +ATF_TC_WITHOUT_HEAD(aligned_dealloc_offmax); +ATF_TC_BODY(aligned_dealloc_offmax, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize; + range.r_len = length = OFF_MAX - offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +/* + * Check unaligned deallocation from certain offset to OFF_MAX + */ +ATF_TC_WITHOUT_HEAD(unaligned_dealloc_offmax); +ATF_TC_BODY(unaligned_dealloc_offmax, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize / 2; + range.r_len = length = OFF_MAX - offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +/* + * Check aligned deallocation around EOF + */ +ATF_TC_WITHOUT_HEAD(aligned_dealloc_eof); +ATF_TC_BODY(aligned_dealloc_eof, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize; + range.r_len = length = (file_max_blocks + 1) * blocksize - + range.r_offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +/* + * Check unaligned deallocation around EOF + */ +ATF_TC_WITHOUT_HEAD(unaligned_dealloc_eof); +ATF_TC_BODY(unaligned_dealloc_eof, tc) +{ + struct spacectl_range range; + off_t offset, length; + blksize_t blocksize; + int fd; + + ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); + range.r_offset = offset = blocksize / 2; + range.r_len = length = file_max_blocks * blocksize + blocksize / 2 - + range.r_offset; + + ATF_REQUIRE((fd = open("sys_fspacectl_testfile", + O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1); + ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0); + ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0, &range) == 0); + ATF_CHECK(check_content_dealloc(fd, offset, length, + file_max_blocks * blocksize) == 0); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, aligned_dealloc); + ATF_TP_ADD_TC(tp, unaligned_dealloc); + ATF_TP_ADD_TC(tp, aligned_dealloc_eof); + ATF_TP_ADD_TC(tp, unaligned_dealloc_eof); + ATF_TP_ADD_TC(tp, aligned_dealloc_offmax); + ATF_TP_ADD_TC(tp, unaligned_dealloc_offmax); + + return atf_no_error(); +} |