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