1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2021 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Ka Ho Ng under sponsorship from 8 * the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 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. 18 * 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 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/mount.h> 34 #include <sys/stat.h> 35 36 #include <atf-c.h> 37 #include <fcntl.h> 38 #include <malloc.h> 39 40 static off_t file_max_blocks = 32; 41 static const char byte_to_fill = 0x5f; 42 43 static int 44 fill(int fd, off_t offset, off_t len) 45 { 46 int error; 47 size_t blen; 48 char *buf; 49 struct stat statbuf; 50 blksize_t blocksize; 51 52 if (fstat(fd, &statbuf) == -1) 53 return (1); 54 blocksize = statbuf.st_blksize; 55 error = 0; 56 buf = malloc(blocksize); 57 if (buf == NULL) 58 return (1); 59 60 while (len > 0) { 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) { 64 error = 1; 65 break; 66 } 67 len -= blen; 68 offset += blen; 69 } 70 71 free(buf); 72 return (error); 73 } 74 75 static blksize_t 76 fd_get_blksize(void) 77 { 78 struct statfs statfsbuf; 79 80 if (statfs(".", &statfsbuf) == -1) 81 return (-1); 82 return statfsbuf.f_iosize; 83 } 84 85 static int 86 check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t file_sz) 87 { 88 int error; 89 size_t blen; 90 off_t offset, resid; 91 struct stat statbuf; 92 char *buf, *sblk; 93 blksize_t blocksize; 94 95 blocksize = fd_get_blksize(); 96 if (blocksize == -1) 97 return (1); 98 error = 0; 99 buf = malloc(blocksize * 2); 100 if (buf == NULL) 101 return (1); 102 sblk = buf + blocksize; 103 104 memset(sblk, 0, blocksize); 105 106 if ((uint64_t)hole_start + hole_len > (uint64_t)file_sz) 107 hole_len = file_sz - hole_start; 108 109 /* 110 * Check hole is zeroed. 111 */ 112 offset = hole_start; 113 resid = hole_len; 114 while (resid > 0) { 115 blen = resid < (off_t)blocksize ? resid : blocksize; 116 if (pread(fd, buf, blen, offset) != (ssize_t)blen) { 117 error = 1; 118 break; 119 } 120 if (memcmp(buf, sblk, blen) != 0) { 121 error = 1; 122 break; 123 } 124 resid -= blen; 125 offset += blen; 126 } 127 128 memset(sblk, byte_to_fill, blocksize); 129 130 /* 131 * Check file region before hole is zeroed. 132 */ 133 offset = 0; 134 resid = hole_start; 135 while (resid > 0) { 136 blen = resid < (off_t)blocksize ? resid : blocksize; 137 if (pread(fd, buf, blen, offset) != (ssize_t)blen) { 138 error = 1; 139 break; 140 } 141 if (memcmp(buf, sblk, blen) != 0) { 142 error = 1; 143 break; 144 } 145 resid -= blen; 146 offset += blen; 147 } 148 149 /* 150 * Check file region after hole is zeroed. 151 */ 152 offset = hole_start + hole_len; 153 resid = file_sz - offset; 154 while (resid > 0) { 155 blen = resid < (off_t)blocksize ? resid : blocksize; 156 if (pread(fd, buf, blen, offset) != (ssize_t)blen) { 157 error = 1; 158 break; 159 } 160 if (memcmp(buf, sblk, blen) != 0) { 161 error = 1; 162 break; 163 } 164 resid -= blen; 165 offset += blen; 166 } 167 168 /* 169 * Check file size matches with expected file size. 170 */ 171 if (fstat(fd, &statbuf) == -1) 172 error = -1; 173 if (statbuf.st_size != file_sz) 174 error = -1; 175 176 free(buf); 177 return (error); 178 } 179 180 /* 181 * Check aligned deallocation 182 */ 183 ATF_TC_WITHOUT_HEAD(aligned_dealloc); 184 ATF_TC_BODY(aligned_dealloc, tc) 185 { 186 struct spacectl_range range; 187 off_t offset, length; 188 blksize_t blocksize; 189 int fd; 190 191 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); 192 range.r_offset = offset = blocksize; 193 range.r_len = length = (file_max_blocks - 1) * blocksize - 194 range.r_offset; 195 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); 203 } 204 205 /* 206 * Check unaligned deallocation 207 */ 208 ATF_TC_WITHOUT_HEAD(unaligned_dealloc); 209 ATF_TC_BODY(unaligned_dealloc, tc) 210 { 211 struct spacectl_range range; 212 off_t offset, length; 213 blksize_t blocksize; 214 int fd; 215 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; 220 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); 228 } 229 230 /* 231 * Check aligned deallocation from certain offset to OFF_MAX 232 */ 233 ATF_TC_WITHOUT_HEAD(aligned_dealloc_offmax); 234 ATF_TC_BODY(aligned_dealloc_offmax, tc) 235 { 236 struct spacectl_range range; 237 off_t offset, length; 238 blksize_t blocksize; 239 int fd; 240 241 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); 242 range.r_offset = offset = blocksize; 243 range.r_len = length = OFF_MAX - offset; 244 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); 252 } 253 254 /* 255 * Check unaligned deallocation from certain offset to OFF_MAX 256 */ 257 ATF_TC_WITHOUT_HEAD(unaligned_dealloc_offmax); 258 ATF_TC_BODY(unaligned_dealloc_offmax, tc) 259 { 260 struct spacectl_range range; 261 off_t offset, length; 262 blksize_t blocksize; 263 int fd; 264 265 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); 266 range.r_offset = offset = blocksize / 2; 267 range.r_len = length = OFF_MAX - offset; 268 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); 276 } 277 278 /* 279 * Check aligned deallocation around EOF 280 */ 281 ATF_TC_WITHOUT_HEAD(aligned_dealloc_eof); 282 ATF_TC_BODY(aligned_dealloc_eof, tc) 283 { 284 struct spacectl_range range; 285 off_t offset, length; 286 blksize_t blocksize; 287 int fd; 288 289 ATF_REQUIRE((blocksize = fd_get_blksize()) != -1); 290 range.r_offset = offset = blocksize; 291 range.r_len = length = (file_max_blocks + 1) * blocksize - 292 range.r_offset; 293 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); 301 } 302 303 /* 304 * Check unaligned deallocation around EOF 305 */ 306 ATF_TC_WITHOUT_HEAD(unaligned_dealloc_eof); 307 ATF_TC_BODY(unaligned_dealloc_eof, tc) 308 { 309 struct spacectl_range range; 310 off_t offset, length; 311 blksize_t blocksize; 312 int fd; 313 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 - 317 range.r_offset; 318 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); 326 } 327 328 ATF_TP_ADD_TCS(tp) 329 { 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); 336 337 return atf_no_error(); 338 } 339