xref: /freebsd/tests/sys/file/fspacectl_test.c (revision ba3c1f5972d7b90feb6e6da47905ff2757e0fe57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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