• 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 "test.h"
34 #include "safe_macros.h"
35 #include "fallocate.h"
36 
37 char *TCID = "fallocate04";
38 int TST_TOTAL = 4;
39 
40 static int fd;
41 static const char fname[] = "fallocate04.txt";
42 static size_t block_size;
43 static size_t buf_size;
44 
45 #define NUM_OF_BLOCKS	3
46 
47 static int verbose;
48 static const option_t options[] = {
49 	{"v", &verbose, NULL},
50 	{NULL, NULL, NULL}
51 };
52 
help(void)53 static void help(void)
54 {
55 	printf("  -v      Verbose\n");
56 }
57 
cleanup(void)58 static void cleanup(void)
59 {
60 	close(fd);
61 	tst_rmdir();
62 }
63 
get_blocksize(void)64 static void get_blocksize(void)
65 {
66 	struct stat file_stat;
67 
68 	SAFE_FSTAT(cleanup, fd, &file_stat);
69 
70 	block_size = file_stat.st_blksize;
71 	buf_size = NUM_OF_BLOCKS * block_size;
72 }
73 
get_allocsize(void)74 static size_t get_allocsize(void)
75 {
76 	struct stat file_stat;
77 
78 	SAFE_FSTAT(cleanup, fd, &file_stat);
79 
80 	return file_stat.st_blocks * 512;
81 }
82 
fill_tst_buf(char buf[])83 static void fill_tst_buf(char buf[])
84 {
85 	/* fill the buffer with a, b, c, ... letters on each block */
86 	int i;
87 
88 	for (i = 0; i < NUM_OF_BLOCKS; ++i)
89 		memset(buf + i * block_size, 'a' + i, block_size);
90 }
91 
setup(void)92 static void setup(void)
93 {
94 	tst_tmpdir();
95 
96 	fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700);
97 
98 	get_blocksize();
99 }
100 
check_file_data(const char exp_buf[],size_t size)101 static void check_file_data(const char exp_buf[], size_t size)
102 {
103 	char rbuf[size];
104 
105 	tst_resm(TINFO, "reading the file, compare with expected buffer");
106 
107 	SAFE_LSEEK(cleanup, fd, 0, SEEK_SET);
108 	SAFE_READ(cleanup, 1, fd, rbuf, size);
109 
110 	if (memcmp(exp_buf, rbuf, size)) {
111 		if (verbose) {
112 			tst_resm_hexd(TINFO, exp_buf, size, "expected:");
113 			tst_resm_hexd(TINFO, rbuf, size, "but read:");
114 		}
115 		tst_brkm(TFAIL, cleanup, "not expected file data");
116 	}
117 }
118 
test01(void)119 static void test01(void)
120 {
121 	tst_resm(TINFO, "allocate '%zu' bytes", buf_size);
122 
123 	if (fallocate(fd, 0, 0, buf_size) == -1) {
124 		if (errno == ENOSYS || errno == EOPNOTSUPP)
125 			tst_brkm(TCONF, cleanup, "fallocate() not supported");
126 		tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed");
127 	}
128 
129 	char buf[buf_size];
130 
131 	fill_tst_buf(buf);
132 
133 	SAFE_WRITE(cleanup, 1, fd, buf, buf_size);
134 
135 	tst_resm(TPASS, "test-case succeeded");
136 }
137 
test02(void)138 static void test02(void)
139 {
140 	size_t alloc_size0 = get_allocsize();
141 
142 	tst_resm(TINFO, "read allocated file size '%zu'", alloc_size0);
143 	tst_resm(TINFO, "make a hole with FALLOC_FL_PUNCH_HOLE");
144 
145 	if (tst_kvercmp(2, 6, 38) < 0) {
146 		tst_brkm(TCONF, cleanup,
147 			 "FALLOC_FL_PUNCH_HOLE needs Linux 2.6.38 or newer");
148 	}
149 
150 	if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
151 	    block_size, block_size) == -1) {
152 		if (errno == EOPNOTSUPP) {
153 			tst_brkm(TCONF, cleanup,
154 			         "FALLOC_FL_PUNCH_HOLE not supported");
155 		}
156 		tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed");
157 	}
158 
159 	tst_resm(TINFO, "check that file has a hole with lseek(,,SEEK_HOLE)");
160 	off_t ret = lseek(fd, 0, SEEK_HOLE);
161 
162 	if (ret != (ssize_t)block_size) {
163 		/* exclude error when kernel doesn't have SEEK_HOLE support */
164 		if (errno != EINVAL) {
165 			tst_brkm(TFAIL | TERRNO, cleanup,
166 				 "fallocate() or lseek() failed");
167 		}
168 		if (tst_kvercmp(3, 1, 0) < 0) {
169 			tst_resm(TINFO, "lseek() doesn't support SEEK_HOLE, "
170 				 "this is expected for < 3.1 kernels");
171 		} else {
172 			tst_brkm(TBROK | TERRNO, cleanup,
173 				 "lseek() doesn't support SEEK_HOLE");
174 		}
175 	} else {
176 		tst_resm(TINFO, "found a hole at '%ld' offset", ret);
177 	}
178 
179 	size_t alloc_size1 = get_allocsize();
180 
181 	tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'",
182 		 alloc_size0, alloc_size1);
183 	if ((alloc_size0 - block_size) != alloc_size1)
184 		tst_brkm(TFAIL, cleanup, "not expected allocated size");
185 
186 	char exp_buf[buf_size];
187 
188 	fill_tst_buf(exp_buf);
189 	memset(exp_buf + block_size, 0, block_size);
190 
191 	check_file_data(exp_buf, buf_size);
192 
193 	tst_resm(TPASS, "test-case succeeded");
194 }
195 
test03(void)196 static void test03(void)
197 {
198 	tst_resm(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE");
199 
200 	if (tst_kvercmp(3, 15, 0) < 0) {
201 		tst_brkm(TCONF, cleanup,
202 			 "FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer");
203 	}
204 
205 	size_t alloc_size0 = get_allocsize();
206 
207 	tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0);
208 
209 	if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size - 1,
210 	    block_size + 2) == -1) {
211 		if (errno == EOPNOTSUPP) {
212 			tst_brkm(TCONF, cleanup,
213 			         "FALLOC_FL_ZERO_RANGE not supported");
214 		}
215 		tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed");
216 	}
217 
218 	/* The file hole in the specified range must be allocated and
219 	 * filled with zeros. Check it.
220 	 */
221 	size_t alloc_size1 = get_allocsize();
222 
223 	tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'",
224 		 alloc_size0, alloc_size1);
225 	if ((alloc_size0 + block_size) != alloc_size1)
226 		tst_brkm(TFAIL, cleanup, "not expected allocated size");
227 
228 	char exp_buf[buf_size];
229 
230 	fill_tst_buf(exp_buf);
231 	memset(exp_buf + block_size - 1, 0, block_size + 2);
232 
233 	check_file_data(exp_buf, buf_size);
234 
235 	tst_resm(TPASS, "test-case succeeded");
236 }
237 
test04(void)238 static void test04(void)
239 {
240 	tst_resm(TINFO, "collapsing file space with FALLOC_FL_COLLAPSE_RANGE");
241 
242 	size_t alloc_size0 = get_allocsize();
243 
244 	tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0);
245 
246 	if (fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, block_size,
247 	    block_size) == -1) {
248 		if (errno == EOPNOTSUPP) {
249 			tst_brkm(TCONF, cleanup,
250 			         "FALLOC_FL_COLLAPSE_RANGE not supported");
251 		}
252 		tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed");
253 	}
254 
255 	size_t alloc_size1 = get_allocsize();
256 
257 	tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'",
258 		 alloc_size0, alloc_size1);
259 	if ((alloc_size0 - block_size) != alloc_size1)
260 		tst_brkm(TFAIL, cleanup, "not expected allocated size");
261 
262 	size_t size = buf_size - block_size;
263 	char tmp_buf[buf_size];
264 	char exp_buf[size];
265 
266 	fill_tst_buf(tmp_buf);
267 
268 	memcpy(exp_buf, tmp_buf, block_size);
269 	memcpy(exp_buf + block_size, tmp_buf + size, block_size);
270 
271 	exp_buf[block_size - 1] = exp_buf[block_size] = '\0';
272 	check_file_data(exp_buf, size);
273 
274 	tst_resm(TPASS, "test-case succeeded");
275 }
276 
main(int argc,char * argv[])277 int main(int argc, char *argv[])
278 {
279 	int lc;
280 
281 	tst_parse_opts(argc, argv, options, help);
282 
283 	setup();
284 
285 	for (lc = 0; TEST_LOOPING(lc); ++lc) {
286 		test01();
287 		test02();
288 		test03();
289 		test04();
290 	}
291 
292 	cleanup();
293 
294 	tst_exit();
295 }
296