1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (c) 2019 SUSE LLC <[email protected]>
4*49cdfc7eSAndroid Build Coastguard Worker */
5*49cdfc7eSAndroid Build Coastguard Worker
6*49cdfc7eSAndroid Build Coastguard Worker /*\
7*49cdfc7eSAndroid Build Coastguard Worker * [Description]
8*49cdfc7eSAndroid Build Coastguard Worker *
9*49cdfc7eSAndroid Build Coastguard Worker * Tests misaligned fallocate()
10*49cdfc7eSAndroid Build Coastguard Worker *
11*49cdfc7eSAndroid Build Coastguard Worker * Test scenario:
12*49cdfc7eSAndroid Build Coastguard Worker *
13*49cdfc7eSAndroid Build Coastguard Worker * 1. write() several blocks worth of data
14*49cdfc7eSAndroid Build Coastguard Worker * 2. fallocate() some more space (not aligned to FS blocks)
15*49cdfc7eSAndroid Build Coastguard Worker * 3. try to write() into the allocated space
16*49cdfc7eSAndroid Build Coastguard Worker * 4. deallocate misaligned part of file range written in step 1
17*49cdfc7eSAndroid Build Coastguard Worker * 5. read() the deallocated range and check that it was zeroed
18*49cdfc7eSAndroid Build Coastguard Worker *
19*49cdfc7eSAndroid Build Coastguard Worker * Subtests:
20*49cdfc7eSAndroid Build Coastguard Worker *
21*49cdfc7eSAndroid Build Coastguard Worker * - fill filesystem between step 2 and 3
22*49cdfc7eSAndroid Build Coastguard Worker * - disable copy-on-write on test file
23*49cdfc7eSAndroid Build Coastguard Worker * - combinations of above subtests
24*49cdfc7eSAndroid Build Coastguard Worker */
25*49cdfc7eSAndroid Build Coastguard Worker
26*49cdfc7eSAndroid Build Coastguard Worker /*
27*49cdfc7eSAndroid Build Coastguard Worker * This is also regression test for:
28*49cdfc7eSAndroid Build Coastguard Worker * e093c4be760e ("xfs: Fix tail rounding in xfs_alloc_file_space()")
29*49cdfc7eSAndroid Build Coastguard Worker * 6d4572a9d71d ("Allow btrfs_truncate_block() to fallback to nocow for data
30*49cdfc7eSAndroid Build Coastguard Worker * space reservation")
31*49cdfc7eSAndroid Build Coastguard Worker */
32*49cdfc7eSAndroid Build Coastguard Worker
33*49cdfc7eSAndroid Build Coastguard Worker #define _GNU_SOURCE
34*49cdfc7eSAndroid Build Coastguard Worker
35*49cdfc7eSAndroid Build Coastguard Worker #include <stdio.h>
36*49cdfc7eSAndroid Build Coastguard Worker #include <stdlib.h>
37*49cdfc7eSAndroid Build Coastguard Worker #include <string.h>
38*49cdfc7eSAndroid Build Coastguard Worker #include <fcntl.h>
39*49cdfc7eSAndroid Build Coastguard Worker #include <sys/ioctl.h>
40*49cdfc7eSAndroid Build Coastguard Worker #include <linux/fs.h>
41*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
42*49cdfc7eSAndroid Build Coastguard Worker #include "lapi/fallocate.h"
43*49cdfc7eSAndroid Build Coastguard Worker
44*49cdfc7eSAndroid Build Coastguard Worker #define MNTPOINT "mntpoint"
45*49cdfc7eSAndroid Build Coastguard Worker #define TEMPFILE MNTPOINT "/test_file"
46*49cdfc7eSAndroid Build Coastguard Worker #define WRITE_BLOCKS 8
47*49cdfc7eSAndroid Build Coastguard Worker #define FALLOCATE_BLOCKS 2
48*49cdfc7eSAndroid Build Coastguard Worker #define DEALLOCATE_BLOCKS 3
49*49cdfc7eSAndroid Build Coastguard Worker #define TESTED_FLAGS "fallocate(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)"
50*49cdfc7eSAndroid Build Coastguard Worker
51*49cdfc7eSAndroid Build Coastguard Worker const struct test_case {
52*49cdfc7eSAndroid Build Coastguard Worker int no_cow, fill_fs;
53*49cdfc7eSAndroid Build Coastguard Worker } testcase_list[] = {
54*49cdfc7eSAndroid Build Coastguard Worker {1, 0},
55*49cdfc7eSAndroid Build Coastguard Worker {1, 1},
56*49cdfc7eSAndroid Build Coastguard Worker {0, 0},
57*49cdfc7eSAndroid Build Coastguard Worker {0, 1}
58*49cdfc7eSAndroid Build Coastguard Worker };
59*49cdfc7eSAndroid Build Coastguard Worker
60*49cdfc7eSAndroid Build Coastguard Worker static int cow_support;
61*49cdfc7eSAndroid Build Coastguard Worker static char *wbuf, *rbuf;
62*49cdfc7eSAndroid Build Coastguard Worker static blksize_t blocksize;
63*49cdfc7eSAndroid Build Coastguard Worker static long wbuf_size, rbuf_size, block_offset;
64*49cdfc7eSAndroid Build Coastguard Worker
toggle_cow(int fd,int enable)65*49cdfc7eSAndroid Build Coastguard Worker static int toggle_cow(int fd, int enable)
66*49cdfc7eSAndroid Build Coastguard Worker {
67*49cdfc7eSAndroid Build Coastguard Worker int ret, attr;
68*49cdfc7eSAndroid Build Coastguard Worker
69*49cdfc7eSAndroid Build Coastguard Worker ret = ioctl(fd, FS_IOC_GETFLAGS, &attr);
70*49cdfc7eSAndroid Build Coastguard Worker
71*49cdfc7eSAndroid Build Coastguard Worker if (ret)
72*49cdfc7eSAndroid Build Coastguard Worker return ret;
73*49cdfc7eSAndroid Build Coastguard Worker
74*49cdfc7eSAndroid Build Coastguard Worker if (enable)
75*49cdfc7eSAndroid Build Coastguard Worker attr &= ~FS_NOCOW_FL;
76*49cdfc7eSAndroid Build Coastguard Worker else
77*49cdfc7eSAndroid Build Coastguard Worker attr |= FS_NOCOW_FL;
78*49cdfc7eSAndroid Build Coastguard Worker
79*49cdfc7eSAndroid Build Coastguard Worker return ioctl(fd, FS_IOC_SETFLAGS, &attr);
80*49cdfc7eSAndroid Build Coastguard Worker }
81*49cdfc7eSAndroid Build Coastguard Worker
setup(void)82*49cdfc7eSAndroid Build Coastguard Worker static void setup(void)
83*49cdfc7eSAndroid Build Coastguard Worker {
84*49cdfc7eSAndroid Build Coastguard Worker unsigned char ch;
85*49cdfc7eSAndroid Build Coastguard Worker long i;
86*49cdfc7eSAndroid Build Coastguard Worker int fd;
87*49cdfc7eSAndroid Build Coastguard Worker struct stat statbuf;
88*49cdfc7eSAndroid Build Coastguard Worker
89*49cdfc7eSAndroid Build Coastguard Worker fd = SAFE_OPEN(TEMPFILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
90*49cdfc7eSAndroid Build Coastguard Worker
91*49cdfc7eSAndroid Build Coastguard Worker /*
92*49cdfc7eSAndroid Build Coastguard Worker * Set FS_NOCOW_FL flag on the temp file. Non-CoW filesystems will
93*49cdfc7eSAndroid Build Coastguard Worker * return error.
94*49cdfc7eSAndroid Build Coastguard Worker */
95*49cdfc7eSAndroid Build Coastguard Worker TEST(toggle_cow(fd, 0));
96*49cdfc7eSAndroid Build Coastguard Worker SAFE_FSTAT(fd, &statbuf);
97*49cdfc7eSAndroid Build Coastguard Worker blocksize = statbuf.st_blksize;
98*49cdfc7eSAndroid Build Coastguard Worker block_offset = MIN(blocksize / 2, (blksize_t)512);
99*49cdfc7eSAndroid Build Coastguard Worker wbuf_size = MAX(WRITE_BLOCKS, FALLOCATE_BLOCKS) * blocksize;
100*49cdfc7eSAndroid Build Coastguard Worker rbuf_size = (DEALLOCATE_BLOCKS + 1) * blocksize;
101*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
102*49cdfc7eSAndroid Build Coastguard Worker SAFE_UNLINK(TEMPFILE);
103*49cdfc7eSAndroid Build Coastguard Worker
104*49cdfc7eSAndroid Build Coastguard Worker if (blocksize < 2)
105*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "Block size %ld too small for test", blocksize);
106*49cdfc7eSAndroid Build Coastguard Worker
107*49cdfc7eSAndroid Build Coastguard Worker if (!TST_RET) {
108*49cdfc7eSAndroid Build Coastguard Worker cow_support = 1;
109*49cdfc7eSAndroid Build Coastguard Worker } else {
110*49cdfc7eSAndroid Build Coastguard Worker switch (TST_ERR) {
111*49cdfc7eSAndroid Build Coastguard Worker case ENOTSUP:
112*49cdfc7eSAndroid Build Coastguard Worker case ENOTTY:
113*49cdfc7eSAndroid Build Coastguard Worker case EINVAL:
114*49cdfc7eSAndroid Build Coastguard Worker case ENOSYS:
115*49cdfc7eSAndroid Build Coastguard Worker cow_support = 0;
116*49cdfc7eSAndroid Build Coastguard Worker break;
117*49cdfc7eSAndroid Build Coastguard Worker
118*49cdfc7eSAndroid Build Coastguard Worker default:
119*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK|TTERRNO,
120*49cdfc7eSAndroid Build Coastguard Worker "Error checking copy-on-write support");
121*49cdfc7eSAndroid Build Coastguard Worker break;
122*49cdfc7eSAndroid Build Coastguard Worker }
123*49cdfc7eSAndroid Build Coastguard Worker }
124*49cdfc7eSAndroid Build Coastguard Worker
125*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Copy-on-write is%s supported",
126*49cdfc7eSAndroid Build Coastguard Worker cow_support ? "" : " not");
127*49cdfc7eSAndroid Build Coastguard Worker wbuf = SAFE_MALLOC(wbuf_size);
128*49cdfc7eSAndroid Build Coastguard Worker rbuf = SAFE_MALLOC(rbuf_size);
129*49cdfc7eSAndroid Build Coastguard Worker
130*49cdfc7eSAndroid Build Coastguard Worker /* Fill the buffer with known values */
131*49cdfc7eSAndroid Build Coastguard Worker for (i = 0, ch = 1; i < wbuf_size; i++, ch++)
132*49cdfc7eSAndroid Build Coastguard Worker wbuf[i] = ch;
133*49cdfc7eSAndroid Build Coastguard Worker }
134*49cdfc7eSAndroid Build Coastguard Worker
check_result(const struct test_case * tc,const char * func,long exp)135*49cdfc7eSAndroid Build Coastguard Worker static int check_result(const struct test_case *tc, const char *func, long exp)
136*49cdfc7eSAndroid Build Coastguard Worker {
137*49cdfc7eSAndroid Build Coastguard Worker if (tc->fill_fs && !tc->no_cow && TST_RET < 0) {
138*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET != -1) {
139*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "%s returned unexpected value %ld",
140*49cdfc7eSAndroid Build Coastguard Worker func, TST_RET);
141*49cdfc7eSAndroid Build Coastguard Worker return 0;
142*49cdfc7eSAndroid Build Coastguard Worker }
143*49cdfc7eSAndroid Build Coastguard Worker
144*49cdfc7eSAndroid Build Coastguard Worker if (TST_ERR != ENOSPC) {
145*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL | TTERRNO, "%s should fail with ENOSPC",
146*49cdfc7eSAndroid Build Coastguard Worker func);
147*49cdfc7eSAndroid Build Coastguard Worker return 0;
148*49cdfc7eSAndroid Build Coastguard Worker }
149*49cdfc7eSAndroid Build Coastguard Worker
150*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS | TTERRNO, "%s on full FS with CoW", func);
151*49cdfc7eSAndroid Build Coastguard Worker return 1;
152*49cdfc7eSAndroid Build Coastguard Worker }
153*49cdfc7eSAndroid Build Coastguard Worker
154*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET < 0) {
155*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL | TTERRNO, "%s failed unexpectedly", func);
156*49cdfc7eSAndroid Build Coastguard Worker return 0;
157*49cdfc7eSAndroid Build Coastguard Worker }
158*49cdfc7eSAndroid Build Coastguard Worker
159*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET != exp) {
160*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL,
161*49cdfc7eSAndroid Build Coastguard Worker "Unexpected return value from %s: %ld (expected %ld)",
162*49cdfc7eSAndroid Build Coastguard Worker func, TST_RET, exp);
163*49cdfc7eSAndroid Build Coastguard Worker return 0;
164*49cdfc7eSAndroid Build Coastguard Worker }
165*49cdfc7eSAndroid Build Coastguard Worker
166*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "%s successful", func);
167*49cdfc7eSAndroid Build Coastguard Worker return 1;
168*49cdfc7eSAndroid Build Coastguard Worker }
169*49cdfc7eSAndroid Build Coastguard Worker
run(unsigned int n)170*49cdfc7eSAndroid Build Coastguard Worker static void run(unsigned int n)
171*49cdfc7eSAndroid Build Coastguard Worker {
172*49cdfc7eSAndroid Build Coastguard Worker int fd, i, err;
173*49cdfc7eSAndroid Build Coastguard Worker long offset, size;
174*49cdfc7eSAndroid Build Coastguard Worker const struct test_case *tc = testcase_list + n;
175*49cdfc7eSAndroid Build Coastguard Worker
176*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Case %u. Fill FS: %s; Use copy on write: %s", n+1,
177*49cdfc7eSAndroid Build Coastguard Worker tc->fill_fs ? "yes" : "no", tc->no_cow ? "no" : "yes");
178*49cdfc7eSAndroid Build Coastguard Worker fd = SAFE_OPEN(TEMPFILE, O_RDWR | O_CREAT | O_TRUNC, 0644);
179*49cdfc7eSAndroid Build Coastguard Worker
180*49cdfc7eSAndroid Build Coastguard Worker if (cow_support)
181*49cdfc7eSAndroid Build Coastguard Worker toggle_cow(fd, !tc->no_cow);
182*49cdfc7eSAndroid Build Coastguard Worker else if (!tc->no_cow)
183*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF, "File system does not support copy-on-write");
184*49cdfc7eSAndroid Build Coastguard Worker
185*49cdfc7eSAndroid Build Coastguard Worker /* Prepare test data for deallocation test */
186*49cdfc7eSAndroid Build Coastguard Worker size = WRITE_BLOCKS * blocksize;
187*49cdfc7eSAndroid Build Coastguard Worker SAFE_WRITE(SAFE_WRITE_ALL, fd, wbuf, size);
188*49cdfc7eSAndroid Build Coastguard Worker
189*49cdfc7eSAndroid Build Coastguard Worker /* Allocation test */
190*49cdfc7eSAndroid Build Coastguard Worker offset = size + block_offset;
191*49cdfc7eSAndroid Build Coastguard Worker size = FALLOCATE_BLOCKS * blocksize;
192*49cdfc7eSAndroid Build Coastguard Worker TEST(fallocate(fd, 0, offset, size));
193*49cdfc7eSAndroid Build Coastguard Worker
194*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET) {
195*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
196*49cdfc7eSAndroid Build Coastguard Worker
197*49cdfc7eSAndroid Build Coastguard Worker if (TST_ERR == ENOTSUP)
198*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF | TTERRNO, "fallocate() not supported");
199*49cdfc7eSAndroid Build Coastguard Worker
200*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TBROK | TTERRNO, "fallocate(fd, 0, %ld, %ld)", offset,
201*49cdfc7eSAndroid Build Coastguard Worker size);
202*49cdfc7eSAndroid Build Coastguard Worker }
203*49cdfc7eSAndroid Build Coastguard Worker
204*49cdfc7eSAndroid Build Coastguard Worker if (tc->fill_fs)
205*49cdfc7eSAndroid Build Coastguard Worker tst_fill_fs(MNTPOINT, 1, TST_FILL_RANDOM);
206*49cdfc7eSAndroid Build Coastguard Worker
207*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(fd, offset, SEEK_SET);
208*49cdfc7eSAndroid Build Coastguard Worker TEST(write(fd, wbuf, size));
209*49cdfc7eSAndroid Build Coastguard Worker if (check_result(tc, "write()", size))
210*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Misaligned allocation works as expected");
211*49cdfc7eSAndroid Build Coastguard Worker
212*49cdfc7eSAndroid Build Coastguard Worker /* Deallocation test */
213*49cdfc7eSAndroid Build Coastguard Worker size = DEALLOCATE_BLOCKS * blocksize;
214*49cdfc7eSAndroid Build Coastguard Worker offset = block_offset;
215*49cdfc7eSAndroid Build Coastguard Worker TEST(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset,
216*49cdfc7eSAndroid Build Coastguard Worker size));
217*49cdfc7eSAndroid Build Coastguard Worker
218*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1 && TST_ERR == ENOTSUP) {
219*49cdfc7eSAndroid Build Coastguard Worker tst_res(TCONF | TTERRNO, TESTED_FLAGS);
220*49cdfc7eSAndroid Build Coastguard Worker goto end;
221*49cdfc7eSAndroid Build Coastguard Worker }
222*49cdfc7eSAndroid Build Coastguard Worker
223*49cdfc7eSAndroid Build Coastguard Worker if (!check_result(tc, TESTED_FLAGS, 0) || TST_RET)
224*49cdfc7eSAndroid Build Coastguard Worker goto end;
225*49cdfc7eSAndroid Build Coastguard Worker
226*49cdfc7eSAndroid Build Coastguard Worker /* Validate that fallocate() cleared the correct file range */
227*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(fd, 0, SEEK_SET);
228*49cdfc7eSAndroid Build Coastguard Worker SAFE_READ(1, fd, rbuf, rbuf_size);
229*49cdfc7eSAndroid Build Coastguard Worker
230*49cdfc7eSAndroid Build Coastguard Worker for (err = 0, i = offset; i < offset + size; i++) {
231*49cdfc7eSAndroid Build Coastguard Worker if (rbuf[i]) {
232*49cdfc7eSAndroid Build Coastguard Worker err = 1;
233*49cdfc7eSAndroid Build Coastguard Worker break;
234*49cdfc7eSAndroid Build Coastguard Worker }
235*49cdfc7eSAndroid Build Coastguard Worker }
236*49cdfc7eSAndroid Build Coastguard Worker
237*49cdfc7eSAndroid Build Coastguard Worker err = err || memcmp(rbuf, wbuf, offset);
238*49cdfc7eSAndroid Build Coastguard Worker offset += size;
239*49cdfc7eSAndroid Build Coastguard Worker size = rbuf_size - offset;
240*49cdfc7eSAndroid Build Coastguard Worker err = err || memcmp(rbuf + offset, wbuf + offset, size);
241*49cdfc7eSAndroid Build Coastguard Worker
242*49cdfc7eSAndroid Build Coastguard Worker if (err)
243*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, TESTED_FLAGS
244*49cdfc7eSAndroid Build Coastguard Worker " did not clear the correct file range.");
245*49cdfc7eSAndroid Build Coastguard Worker else
246*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, TESTED_FLAGS " cleared the correct file range");
247*49cdfc7eSAndroid Build Coastguard Worker
248*49cdfc7eSAndroid Build Coastguard Worker end:
249*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
250*49cdfc7eSAndroid Build Coastguard Worker tst_purge_dir(MNTPOINT);
251*49cdfc7eSAndroid Build Coastguard Worker }
252*49cdfc7eSAndroid Build Coastguard Worker
cleanup(void)253*49cdfc7eSAndroid Build Coastguard Worker static void cleanup(void)
254*49cdfc7eSAndroid Build Coastguard Worker {
255*49cdfc7eSAndroid Build Coastguard Worker free(wbuf);
256*49cdfc7eSAndroid Build Coastguard Worker free(rbuf);
257*49cdfc7eSAndroid Build Coastguard Worker }
258*49cdfc7eSAndroid Build Coastguard Worker
259*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
260*49cdfc7eSAndroid Build Coastguard Worker .test = run,
261*49cdfc7eSAndroid Build Coastguard Worker .tcnt = ARRAY_SIZE(testcase_list),
262*49cdfc7eSAndroid Build Coastguard Worker .needs_root = 1,
263*49cdfc7eSAndroid Build Coastguard Worker .dev_min_size = 1024,
264*49cdfc7eSAndroid Build Coastguard Worker .max_runtime = 120,
265*49cdfc7eSAndroid Build Coastguard Worker .mount_device = 1,
266*49cdfc7eSAndroid Build Coastguard Worker .mntpoint = MNTPOINT,
267*49cdfc7eSAndroid Build Coastguard Worker .all_filesystems = 1,
268*49cdfc7eSAndroid Build Coastguard Worker .setup = setup,
269*49cdfc7eSAndroid Build Coastguard Worker .cleanup = cleanup,
270*49cdfc7eSAndroid Build Coastguard Worker .tags = (const struct tst_tag[]) {
271*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "e093c4be760e"},
272*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "6d4572a9d71d"},
273*49cdfc7eSAndroid Build Coastguard Worker {}
274*49cdfc7eSAndroid Build Coastguard Worker }
275*49cdfc7eSAndroid Build Coastguard Worker };
276