• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015 Oracle and/or its affiliates. All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Description:
18  * Test allocates a file with specified size then tests the following modes:
19  * FALLOC_FL_PUNCH_HOLE, FALLOC_FL_ZERO_RANGE and FALLOC_FL_COLLAPSE_RANGE.
20  *
21  * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
22  */
23 
24 #define _GNU_SOURCE
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 
33 #include "tst_test.h"
34 #include "lapi/fallocate.h"
35 
36 static int fd;
37 static size_t block_size;
38 static size_t buf_size;
39 
40 #define MNTPOINT "fallocate"
41 #define FNAME MNTPOINT "/fallocate.txt"
42 #define NUM_OF_BLOCKS	3
43 
44 static char *verbose;
45 
get_blocksize(void)46 static void get_blocksize(void)
47 {
48 	struct stat file_stat;
49 
50 	SAFE_FSTAT(fd, &file_stat);
51 
52 	block_size = file_stat.st_blksize;
53 	buf_size = NUM_OF_BLOCKS * block_size;
54 }
55 
get_allocsize(void)56 static size_t get_allocsize(void)
57 {
58 	struct stat file_stat;
59 
60 	fsync(fd);
61 
62 	SAFE_FSTAT(fd, &file_stat);
63 
64 	return file_stat.st_blocks * 512;
65 }
66 
fill_tst_buf(char buf[])67 static void fill_tst_buf(char buf[])
68 {
69 	/* fill the buffer with a, b, c, ... letters on each block */
70 	int i;
71 
72 	for (i = 0; i < NUM_OF_BLOCKS; ++i)
73 		memset(buf + i * block_size, 'a' + i, block_size);
74 }
75 
check_file_data(const char exp_buf[],size_t size)76 static void check_file_data(const char exp_buf[], size_t size)
77 {
78 	char rbuf[size];
79 
80 	tst_res(TINFO, "reading the file, compare with expected buffer");
81 
82 	SAFE_LSEEK(fd, 0, SEEK_SET);
83 	SAFE_READ(1, fd, rbuf, size);
84 
85 	if (memcmp(exp_buf, rbuf, size)) {
86 		if (verbose) {
87 			tst_res_hexd(TINFO, exp_buf, size, "expected:");
88 			tst_res_hexd(TINFO, rbuf, size, "but read:");
89 		}
90 		tst_brk(TFAIL, "not expected file data");
91 	}
92 }
93 
test01(void)94 static void test01(void)
95 {
96 	tst_res(TINFO, "allocate '%zu' bytes", buf_size);
97 
98 	if (fallocate(fd, 0, 0, buf_size) == -1) {
99 		if (errno == ENOSYS || errno == EOPNOTSUPP)
100 			tst_brk(TCONF, "fallocate() not supported");
101 		tst_brk(TFAIL | TERRNO, "fallocate() failed");
102 	}
103 
104 	char buf[buf_size];
105 
106 	fill_tst_buf(buf);
107 
108 	SAFE_WRITE(1, fd, buf, buf_size);
109 
110 	tst_res(TPASS, "test-case succeeded");
111 }
112 
test02(void)113 static void test02(void)
114 {
115 	size_t alloc_size0 = get_allocsize();
116 
117 	tst_res(TINFO, "read allocated file size '%zu'", alloc_size0);
118 	tst_res(TINFO, "make a hole with FALLOC_FL_PUNCH_HOLE");
119 
120 	if (tst_kvercmp(2, 6, 38) < 0) {
121 		tst_brk(TCONF,
122 			"FALLOC_FL_PUNCH_HOLE needs Linux 2.6.38 or newer");
123 	}
124 
125 	if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
126 	    block_size, block_size) == -1) {
127 		if (errno == EOPNOTSUPP) {
128 			tst_brk(TCONF,
129 			        "FALLOC_FL_PUNCH_HOLE not supported");
130 		}
131 		tst_brk(TFAIL | TERRNO, "fallocate() failed");
132 	}
133 
134 	tst_res(TINFO, "check that file has a hole with lseek(,,SEEK_HOLE)");
135 	off_t ret = lseek(fd, 0, SEEK_HOLE);
136 
137 	if (ret != (ssize_t)block_size) {
138 		/* exclude error when kernel doesn't have SEEK_HOLE support */
139 		if (errno != EINVAL) {
140 			tst_brk(TFAIL | TERRNO,
141 				 "fallocate() or lseek() failed");
142 		}
143 		if (tst_kvercmp(3, 1, 0) < 0) {
144 			tst_res(TINFO, "lseek() doesn't support SEEK_HOLE, "
145 				 "this is expected for < 3.1 kernels");
146 		} else {
147 			tst_brk(TBROK | TERRNO,
148 				 "lseek() doesn't support SEEK_HOLE");
149 		}
150 	} else {
151 		tst_res(TINFO, "found a hole at '%ld' offset", ret);
152 	}
153 
154 	size_t alloc_size1 = get_allocsize();
155 
156 	tst_res(TINFO, "allocated file size before '%zu' and after '%zu'",
157 		 alloc_size0, alloc_size1);
158 	if ((alloc_size0 - block_size) != alloc_size1)
159 		tst_brk(TFAIL, "not expected allocated size");
160 
161 	char exp_buf[buf_size];
162 
163 	fill_tst_buf(exp_buf);
164 	memset(exp_buf + block_size, 0, block_size);
165 
166 	check_file_data(exp_buf, buf_size);
167 
168 	tst_res(TPASS, "test-case succeeded");
169 }
170 
test03(void)171 static void test03(void)
172 {
173 	tst_res(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE");
174 
175 	if (tst_kvercmp(3, 15, 0) < 0) {
176 		tst_brk(TCONF,
177 			"FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer");
178 	}
179 
180 	size_t alloc_size0 = get_allocsize();
181 
182 	tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0);
183 
184 	if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size - 1,
185 	    block_size + 2) == -1) {
186 		if (errno == EOPNOTSUPP) {
187 			tst_brk(TCONF,
188 			        "FALLOC_FL_ZERO_RANGE not supported");
189 		}
190 		tst_brk(TFAIL | TERRNO, "fallocate failed");
191 	}
192 
193 	/* The file hole in the specified range must be allocated and
194 	 * filled with zeros. Check it.
195 	 */
196 	size_t alloc_size1 = get_allocsize();
197 
198 	tst_res(TINFO, "allocated file size before '%zu' and after '%zu'",
199 		 alloc_size0, alloc_size1);
200 	if ((alloc_size0 + block_size) != alloc_size1)
201 		tst_brk(TFAIL, "not expected allocated size");
202 
203 	char exp_buf[buf_size];
204 
205 	fill_tst_buf(exp_buf);
206 	memset(exp_buf + block_size - 1, 0, block_size + 2);
207 
208 	check_file_data(exp_buf, buf_size);
209 
210 	tst_res(TPASS, "test-case succeeded");
211 }
212 
test04(void)213 static void test04(void)
214 {
215 	tst_res(TINFO, "collapsing file space with FALLOC_FL_COLLAPSE_RANGE");
216 
217 	size_t alloc_size0 = get_allocsize();
218 
219 	tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0);
220 
221 	if (fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, block_size,
222 	    block_size) == -1) {
223 		if (errno == EOPNOTSUPP) {
224 			tst_brk(TCONF,
225 			        "FALLOC_FL_COLLAPSE_RANGE not supported");
226 		}
227 		tst_brk(TFAIL | TERRNO, "fallocate failed");
228 	}
229 
230 	size_t alloc_size1 = get_allocsize();
231 
232 	tst_res(TINFO, "allocated file size before '%zu' and after '%zu'",
233 		 alloc_size0, alloc_size1);
234 	if ((alloc_size0 - block_size) != alloc_size1)
235 		tst_brk(TFAIL, "not expected allocated size");
236 
237 	size_t size = buf_size - block_size;
238 	char tmp_buf[buf_size];
239 	char exp_buf[size];
240 
241 	fill_tst_buf(tmp_buf);
242 
243 	memcpy(exp_buf, tmp_buf, block_size);
244 	memcpy(exp_buf + block_size, tmp_buf + 2 * block_size,
245 	       buf_size - block_size * 2);
246 
247 	exp_buf[block_size - 1] = exp_buf[block_size] = '\0';
248 	check_file_data(exp_buf, size);
249 
250 	tst_res(TPASS, "test-case succeeded");
251 }
252 
test05(void)253 static void test05(void)
254 {
255 	tst_res(TINFO, "inserting space with FALLOC_FL_INSERT_RANGE");
256 
257 	size_t alloc_size0 = get_allocsize();
258 
259 	tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0);
260 
261 	if (fallocate(fd, FALLOC_FL_INSERT_RANGE, block_size,
262 	    block_size) == -1) {
263 		if (errno == EOPNOTSUPP) {
264 			tst_brk(TCONF,
265 				"FALLOC_FL_INSERT_RANGE not supported");
266 		}
267 		tst_brk(TFAIL | TERRNO, "fallocate failed");
268 	}
269 
270 	/* allocate space and ensure that it filled with zeroes */
271 	if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size, block_size) == -1)
272 		tst_brk(TFAIL | TERRNO, "fallocate failed");
273 
274 	size_t alloc_size1 = get_allocsize();
275 
276 	tst_res(TINFO, "allocated file size before '%zu' and after '%zu'",
277 		 alloc_size0, alloc_size1);
278 	if ((alloc_size0 + block_size) != alloc_size1)
279 		tst_brk(TFAIL, "not expected allocated size");
280 
281 	char exp_buf[buf_size];
282 
283 	fill_tst_buf(exp_buf);
284 	memset(exp_buf + block_size - 1, 0, block_size + 2);
285 
286 	check_file_data(exp_buf, buf_size);
287 
288 	tst_res(TPASS, "test-case succeeded");
289 }
290 
291 static void (*tcases[])(void) = {
292 	test01, test02, test03, test04, test05
293 };
294 
run(unsigned int i)295 static void run(unsigned int i)
296 {
297 	tcases[i]();
298 }
299 
setup(void)300 static void setup(void)
301 {
302 	fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0700);
303 
304 	get_blocksize();
305 }
306 
cleanup(void)307 static void cleanup(void)
308 {
309 	if (fd > 0)
310 		SAFE_CLOSE(fd);
311 }
312 
313 static struct tst_option opts[] = {
314 	{"v", &verbose, "-v       Turns on verbose mode"},
315 	{NULL, NULL, NULL}
316 };
317 
318 static struct tst_test test = {
319 	.options = opts,
320 	.cleanup = cleanup,
321 	.setup = setup,
322 	.test = run,
323 	.tcnt = ARRAY_SIZE(tcases),
324 	.mount_device = 1,
325 	.mntpoint = MNTPOINT,
326 	.all_filesystems = 1,
327 	.needs_tmpdir = 1,
328 	.needs_root = 1,
329 };
330