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