xref: /linux/tools/testing/selftests/filesystems/fuse/write_extend_eof_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*60dbcce1SJimmy Zuber // SPDX-License-Identifier: GPL-2.0
2*60dbcce1SJimmy Zuber /*
3*60dbcce1SJimmy Zuber  * Regression test for the fuse write-extend partial-EOF-page zeroing bug.
4*60dbcce1SJimmy Zuber  *
5*60dbcce1SJimmy Zuber  * A buffered write that extends i_size past a non-page-aligned EOF must zero
6*60dbcce1SJimmy Zuber  * the tail of the old last page.  If an application has mmap'd that page and
7*60dbcce1SJimmy Zuber  * stored into the post-EOF region (undefined until the file grows), the
8*60dbcce1SJimmy Zuber  * now-in-bounds tail must read back as zero, not as the stale stored bytes.
9*60dbcce1SJimmy Zuber  *
10*60dbcce1SJimmy Zuber  * The bug is exposed on a non-writeback_cache server that keeps the page cache
11*60dbcce1SJimmy Zuber  * across the write (FOPEN_KEEP_CACHE without FOPEN_DIRECT_IO).  This test is a
12*60dbcce1SJimmy Zuber  * raw /dev/fuse server in that mode; the backing data is always zero in the
13*60dbcce1SJimmy Zuber  * hole, so any non-zero byte a read sees is stale page-cache data.
14*60dbcce1SJimmy Zuber  *
15*60dbcce1SJimmy Zuber  * Requires root to mount fuse.
16*60dbcce1SJimmy Zuber  */
17*60dbcce1SJimmy Zuber #define _GNU_SOURCE
18*60dbcce1SJimmy Zuber #include <errno.h>
19*60dbcce1SJimmy Zuber #include <fcntl.h>
20*60dbcce1SJimmy Zuber #include <linux/falloc.h>
21*60dbcce1SJimmy Zuber #include <pthread.h>
22*60dbcce1SJimmy Zuber #include <stdint.h>
23*60dbcce1SJimmy Zuber #include <string.h>
24*60dbcce1SJimmy Zuber #include <unistd.h>
25*60dbcce1SJimmy Zuber #include <sys/mman.h>
26*60dbcce1SJimmy Zuber #include <sys/mount.h>
27*60dbcce1SJimmy Zuber #include <sys/stat.h>
28*60dbcce1SJimmy Zuber #include <sys/uio.h>
29*60dbcce1SJimmy Zuber #include <linux/fuse.h>
30*60dbcce1SJimmy Zuber 
31*60dbcce1SJimmy Zuber #include "../../kselftest_harness.h"
32*60dbcce1SJimmy Zuber 
33*60dbcce1SJimmy Zuber #define FUSE_ROOT_ID	1
34*60dbcce1SJimmy Zuber #define FILE_INO	2
35*60dbcce1SJimmy Zuber #define MAX_WRITE	(128 * 1024)
36*60dbcce1SJimmy Zuber #define BACKING_SIZE	(4 * 1024 * 1024)
37*60dbcce1SJimmy Zuber #define POLLUTE		0xee
38*60dbcce1SJimmy Zuber 
39*60dbcce1SJimmy Zuber /* Server-side state, shared with the responder thread. */
40*60dbcce1SJimmy Zuber struct server {
41*60dbcce1SJimmy Zuber 	int fd;
42*60dbcce1SJimmy Zuber 	unsigned char backing[BACKING_SIZE];	/* authoritative bytes */
43*60dbcce1SJimmy Zuber 	uint64_t size;
44*60dbcce1SJimmy Zuber };
45*60dbcce1SJimmy Zuber 
46*60dbcce1SJimmy Zuber static void reply(int fd, uint64_t unique, int error, void *data, size_t len)
47*60dbcce1SJimmy Zuber {
48*60dbcce1SJimmy Zuber 	struct fuse_out_header oh = {
49*60dbcce1SJimmy Zuber 		.len = sizeof(oh) + (data ? len : 0),
50*60dbcce1SJimmy Zuber 		.error = error,
51*60dbcce1SJimmy Zuber 		.unique = unique,
52*60dbcce1SJimmy Zuber 	};
53*60dbcce1SJimmy Zuber 	struct iovec iov[2] = { { &oh, sizeof(oh) }, { data, len } };
54*60dbcce1SJimmy Zuber 
55*60dbcce1SJimmy Zuber 	/* Errors here are teardown races (device closed on unmount); ignore. */
56*60dbcce1SJimmy Zuber 	if (writev(fd, iov, data ? 2 : 1) < 0)
57*60dbcce1SJimmy Zuber 		return;
58*60dbcce1SJimmy Zuber }
59*60dbcce1SJimmy Zuber 
60*60dbcce1SJimmy Zuber static void fill_attr(struct fuse_attr *a, uint64_t ino, uint32_t mode,
61*60dbcce1SJimmy Zuber 		      uint64_t size)
62*60dbcce1SJimmy Zuber {
63*60dbcce1SJimmy Zuber 	memset(a, 0, sizeof(*a));
64*60dbcce1SJimmy Zuber 	a->ino = ino;
65*60dbcce1SJimmy Zuber 	a->mode = mode;
66*60dbcce1SJimmy Zuber 	a->nlink = 1;
67*60dbcce1SJimmy Zuber 	a->size = size;
68*60dbcce1SJimmy Zuber 	a->blksize = sysconf(_SC_PAGESIZE);
69*60dbcce1SJimmy Zuber }
70*60dbcce1SJimmy Zuber 
71*60dbcce1SJimmy Zuber static void *server_thread(void *arg)
72*60dbcce1SJimmy Zuber {
73*60dbcce1SJimmy Zuber 	struct server *s = arg;
74*60dbcce1SJimmy Zuber 	static char buf[MAX_WRITE + 4096];
75*60dbcce1SJimmy Zuber 
76*60dbcce1SJimmy Zuber 	for (;;) {
77*60dbcce1SJimmy Zuber 		ssize_t n = read(s->fd, buf, sizeof(buf));
78*60dbcce1SJimmy Zuber 		struct fuse_in_header *ih = (void *)buf;
79*60dbcce1SJimmy Zuber 
80*60dbcce1SJimmy Zuber 		if (n < 0) {
81*60dbcce1SJimmy Zuber 			if (errno == EINTR || errno == EAGAIN)
82*60dbcce1SJimmy Zuber 				continue;
83*60dbcce1SJimmy Zuber 			return NULL;	/* device closed on unmount */
84*60dbcce1SJimmy Zuber 		}
85*60dbcce1SJimmy Zuber 		if (n < (ssize_t)sizeof(*ih))
86*60dbcce1SJimmy Zuber 			continue;
87*60dbcce1SJimmy Zuber 
88*60dbcce1SJimmy Zuber 		switch (ih->opcode) {
89*60dbcce1SJimmy Zuber 		case FUSE_INIT: {
90*60dbcce1SJimmy Zuber 			struct fuse_init_in *in = (void *)(ih + 1);
91*60dbcce1SJimmy Zuber 			struct fuse_init_out out = {0};
92*60dbcce1SJimmy Zuber 
93*60dbcce1SJimmy Zuber 			/* No FUSE_WRITEBACK_CACHE: the exposed configuration. */
94*60dbcce1SJimmy Zuber 			out.major = FUSE_KERNEL_VERSION;
95*60dbcce1SJimmy Zuber 			out.minor = FUSE_KERNEL_MINOR_VERSION;
96*60dbcce1SJimmy Zuber 			out.max_readahead = in->max_readahead;
97*60dbcce1SJimmy Zuber 			out.max_write = MAX_WRITE;
98*60dbcce1SJimmy Zuber 			out.max_background = 16;
99*60dbcce1SJimmy Zuber 			out.congestion_threshold = 12;
100*60dbcce1SJimmy Zuber 			out.flags = FUSE_MAX_PAGES;
101*60dbcce1SJimmy Zuber 			out.max_pages = MAX_WRITE / sysconf(_SC_PAGESIZE);
102*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
103*60dbcce1SJimmy Zuber 			break;
104*60dbcce1SJimmy Zuber 		}
105*60dbcce1SJimmy Zuber 		case FUSE_GETATTR: {
106*60dbcce1SJimmy Zuber 			struct fuse_attr_out out = {0};
107*60dbcce1SJimmy Zuber 			int root = ih->nodeid == FUSE_ROOT_ID;
108*60dbcce1SJimmy Zuber 
109*60dbcce1SJimmy Zuber 			out.attr_valid = 3600;
110*60dbcce1SJimmy Zuber 			fill_attr(&out.attr, ih->nodeid,
111*60dbcce1SJimmy Zuber 				  root ? (S_IFDIR | 0755) : (S_IFREG | 0644),
112*60dbcce1SJimmy Zuber 				  root ? 0 : s->size);
113*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
114*60dbcce1SJimmy Zuber 			break;
115*60dbcce1SJimmy Zuber 		}
116*60dbcce1SJimmy Zuber 		case FUSE_LOOKUP: {
117*60dbcce1SJimmy Zuber 			struct fuse_entry_out out = {0};
118*60dbcce1SJimmy Zuber 
119*60dbcce1SJimmy Zuber 			out.nodeid = FILE_INO;
120*60dbcce1SJimmy Zuber 			out.attr_valid = 3600;
121*60dbcce1SJimmy Zuber 			out.entry_valid = 3600;
122*60dbcce1SJimmy Zuber 			fill_attr(&out.attr, FILE_INO, S_IFREG | 0644, s->size);
123*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
124*60dbcce1SJimmy Zuber 			break;
125*60dbcce1SJimmy Zuber 		}
126*60dbcce1SJimmy Zuber 		case FUSE_OPEN:
127*60dbcce1SJimmy Zuber 		case FUSE_OPENDIR: {
128*60dbcce1SJimmy Zuber 			struct fuse_open_out out = {0};
129*60dbcce1SJimmy Zuber 
130*60dbcce1SJimmy Zuber 			/* Keep the cache across the write, but not direct I/O. */
131*60dbcce1SJimmy Zuber 			out.open_flags = FOPEN_KEEP_CACHE;
132*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
133*60dbcce1SJimmy Zuber 			break;
134*60dbcce1SJimmy Zuber 		}
135*60dbcce1SJimmy Zuber 		case FUSE_READ: {
136*60dbcce1SJimmy Zuber 			struct fuse_read_in *in = (void *)(ih + 1);
137*60dbcce1SJimmy Zuber 			uint64_t off = in->offset;
138*60dbcce1SJimmy Zuber 			uint32_t size = in->size;
139*60dbcce1SJimmy Zuber 
140*60dbcce1SJimmy Zuber 			if (off >= BACKING_SIZE)
141*60dbcce1SJimmy Zuber 				size = 0;
142*60dbcce1SJimmy Zuber 			else if (off + size > BACKING_SIZE)
143*60dbcce1SJimmy Zuber 				size = BACKING_SIZE - off;
144*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, s->backing + off, size);
145*60dbcce1SJimmy Zuber 			break;
146*60dbcce1SJimmy Zuber 		}
147*60dbcce1SJimmy Zuber 		case FUSE_WRITE: {
148*60dbcce1SJimmy Zuber 			struct fuse_write_in *in = (void *)(ih + 1);
149*60dbcce1SJimmy Zuber 			struct fuse_write_out out = {0};
150*60dbcce1SJimmy Zuber 			uint64_t off = in->offset;
151*60dbcce1SJimmy Zuber 			uint32_t size = in->size;
152*60dbcce1SJimmy Zuber 
153*60dbcce1SJimmy Zuber 			if (off < BACKING_SIZE) {
154*60dbcce1SJimmy Zuber 				uint32_t c = size;
155*60dbcce1SJimmy Zuber 
156*60dbcce1SJimmy Zuber 				if (off + c > BACKING_SIZE)
157*60dbcce1SJimmy Zuber 					c = BACKING_SIZE - off;
158*60dbcce1SJimmy Zuber 				memcpy(s->backing + off, in + 1, c);
159*60dbcce1SJimmy Zuber 				if (off + c > s->size)
160*60dbcce1SJimmy Zuber 					s->size = off + c;
161*60dbcce1SJimmy Zuber 			}
162*60dbcce1SJimmy Zuber 			out.size = size;
163*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
164*60dbcce1SJimmy Zuber 			break;
165*60dbcce1SJimmy Zuber 		}
166*60dbcce1SJimmy Zuber 		case FUSE_SETATTR: {
167*60dbcce1SJimmy Zuber 			struct fuse_setattr_in *in = (void *)(ih + 1);
168*60dbcce1SJimmy Zuber 			struct fuse_attr_out out = {0};
169*60dbcce1SJimmy Zuber 
170*60dbcce1SJimmy Zuber 			if ((in->valid & FATTR_SIZE) && in->size <= BACKING_SIZE) {
171*60dbcce1SJimmy Zuber 				if (in->size > s->size)
172*60dbcce1SJimmy Zuber 					memset(s->backing + s->size, 0,
173*60dbcce1SJimmy Zuber 					       in->size - s->size);
174*60dbcce1SJimmy Zuber 				s->size = in->size;
175*60dbcce1SJimmy Zuber 			}
176*60dbcce1SJimmy Zuber 			out.attr_valid = 3600;
177*60dbcce1SJimmy Zuber 			fill_attr(&out.attr, ih->nodeid, S_IFREG | 0644, s->size);
178*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, &out, sizeof(out));
179*60dbcce1SJimmy Zuber 			break;
180*60dbcce1SJimmy Zuber 		}
181*60dbcce1SJimmy Zuber 		case FUSE_FALLOCATE: {
182*60dbcce1SJimmy Zuber 			struct fuse_fallocate_in *in = (void *)(ih + 1);
183*60dbcce1SJimmy Zuber 			uint64_t end = in->offset + in->length;
184*60dbcce1SJimmy Zuber 
185*60dbcce1SJimmy Zuber 			/* Only plain (size-extending) fallocate is used here. */
186*60dbcce1SJimmy Zuber 			if (!(in->mode & FALLOC_FL_KEEP_SIZE) &&
187*60dbcce1SJimmy Zuber 			    end <= BACKING_SIZE && end > s->size) {
188*60dbcce1SJimmy Zuber 				memset(s->backing + s->size, 0, end - s->size);
189*60dbcce1SJimmy Zuber 				s->size = end;
190*60dbcce1SJimmy Zuber 			}
191*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, NULL, 0);
192*60dbcce1SJimmy Zuber 			break;
193*60dbcce1SJimmy Zuber 		}
194*60dbcce1SJimmy Zuber 		case FUSE_FLUSH:
195*60dbcce1SJimmy Zuber 		case FUSE_RELEASE:
196*60dbcce1SJimmy Zuber 		case FUSE_RELEASEDIR:
197*60dbcce1SJimmy Zuber 		case FUSE_FSYNC:
198*60dbcce1SJimmy Zuber 		case FUSE_ACCESS:
199*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, 0, NULL, 0);
200*60dbcce1SJimmy Zuber 			break;
201*60dbcce1SJimmy Zuber 		case FUSE_FORGET:
202*60dbcce1SJimmy Zuber 			break;
203*60dbcce1SJimmy Zuber 		default:
204*60dbcce1SJimmy Zuber 			reply(s->fd, ih->unique, -EOPNOTSUPP, NULL, 0);
205*60dbcce1SJimmy Zuber 			break;
206*60dbcce1SJimmy Zuber 		}
207*60dbcce1SJimmy Zuber 	}
208*60dbcce1SJimmy Zuber }
209*60dbcce1SJimmy Zuber 
210*60dbcce1SJimmy Zuber FIXTURE(fuse)
211*60dbcce1SJimmy Zuber {
212*60dbcce1SJimmy Zuber 	struct server *srv;
213*60dbcce1SJimmy Zuber 	pthread_t thread;
214*60dbcce1SJimmy Zuber 	char dir[64];
215*60dbcce1SJimmy Zuber 	long page;		/* runtime page size */
216*60dbcce1SJimmy Zuber 	off_t eof;		/* mid-page EOF, page-relative */
217*60dbcce1SJimmy Zuber 	int fd;			/* open test file */
218*60dbcce1SJimmy Zuber 	char *map;		/* mmap of the EOF page */
219*60dbcce1SJimmy Zuber 	int mounted;
220*60dbcce1SJimmy Zuber };
221*60dbcce1SJimmy Zuber 
222*60dbcce1SJimmy Zuber FIXTURE_SETUP(fuse)
223*60dbcce1SJimmy Zuber {
224*60dbcce1SJimmy Zuber 	char opts[128];
225*60dbcce1SJimmy Zuber 	pthread_t t;
226*60dbcce1SJimmy Zuber 
227*60dbcce1SJimmy Zuber 	if (geteuid() != 0)
228*60dbcce1SJimmy Zuber 		SKIP(return, "need root to mount fuse");
229*60dbcce1SJimmy Zuber 
230*60dbcce1SJimmy Zuber 	self->page = sysconf(_SC_PAGESIZE);
231*60dbcce1SJimmy Zuber 	self->fd = -1;
232*60dbcce1SJimmy Zuber 	self->map = MAP_FAILED;
233*60dbcce1SJimmy Zuber 
234*60dbcce1SJimmy Zuber 	self->srv = mmap(NULL, sizeof(*self->srv), PROT_READ | PROT_WRITE,
235*60dbcce1SJimmy Zuber 			 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
236*60dbcce1SJimmy Zuber 	ASSERT_NE(MAP_FAILED, self->srv);
237*60dbcce1SJimmy Zuber 
238*60dbcce1SJimmy Zuber 	self->srv->fd = open("/dev/fuse", O_RDWR);
239*60dbcce1SJimmy Zuber 	ASSERT_GE(self->srv->fd, 0);
240*60dbcce1SJimmy Zuber 
241*60dbcce1SJimmy Zuber 	strcpy(self->dir, "/tmp/fuse_weof_XXXXXX");
242*60dbcce1SJimmy Zuber 	ASSERT_NE(NULL, mkdtemp(self->dir));
243*60dbcce1SJimmy Zuber 
244*60dbcce1SJimmy Zuber 	snprintf(opts, sizeof(opts),
245*60dbcce1SJimmy Zuber 		 "fd=%d,rootmode=40000,user_id=0,group_id=0",
246*60dbcce1SJimmy Zuber 		 self->srv->fd);
247*60dbcce1SJimmy Zuber 	ASSERT_EQ(0, mount("fuse", self->dir, "fuse", 0, opts));
248*60dbcce1SJimmy Zuber 	self->mounted = 1;
249*60dbcce1SJimmy Zuber 
250*60dbcce1SJimmy Zuber 	ASSERT_EQ(0, pthread_create(&t, NULL, server_thread, self->srv));
251*60dbcce1SJimmy Zuber 	self->thread = t;
252*60dbcce1SJimmy Zuber }
253*60dbcce1SJimmy Zuber 
254*60dbcce1SJimmy Zuber FIXTURE_TEARDOWN(fuse)
255*60dbcce1SJimmy Zuber {
256*60dbcce1SJimmy Zuber 	if (self->map != MAP_FAILED)
257*60dbcce1SJimmy Zuber 		munmap(self->map, self->page);
258*60dbcce1SJimmy Zuber 	if (self->fd >= 0)
259*60dbcce1SJimmy Zuber 		close(self->fd);
260*60dbcce1SJimmy Zuber 	if (self->mounted)
261*60dbcce1SJimmy Zuber 		umount2(self->dir, MNT_DETACH);
262*60dbcce1SJimmy Zuber 	if (self->srv && self->srv != MAP_FAILED) {
263*60dbcce1SJimmy Zuber 		if (self->srv->fd > 0)
264*60dbcce1SJimmy Zuber 			close(self->srv->fd);
265*60dbcce1SJimmy Zuber 		munmap(self->srv, sizeof(*self->srv));
266*60dbcce1SJimmy Zuber 	}
267*60dbcce1SJimmy Zuber 	if (self->dir[0])
268*60dbcce1SJimmy Zuber 		rmdir(self->dir);
269*60dbcce1SJimmy Zuber }
270*60dbcce1SJimmy Zuber 
271*60dbcce1SJimmy Zuber /*
272*60dbcce1SJimmy Zuber  * Create the test file with a mid-page EOF and mmap-store POLLUTE into its
273*60dbcce1SJimmy Zuber  * post-EOF tail (a legal store, undefined until the file grows).  Leaves the
274*60dbcce1SJimmy Zuber  * file open and the EOF page mapped in the fixture for the caller to extend.
275*60dbcce1SJimmy Zuber  */
276*60dbcce1SJimmy Zuber static void pollute_eof_tail(struct __test_metadata *_metadata,
277*60dbcce1SJimmy Zuber 			     FIXTURE_DATA(fuse) * self)
278*60dbcce1SJimmy Zuber {
279*60dbcce1SJimmy Zuber 	off_t eof = 2 * self->page + self->page / 4;
280*60dbcce1SJimmy Zuber 	char path[128];
281*60dbcce1SJimmy Zuber 	char *buf;
282*60dbcce1SJimmy Zuber 
283*60dbcce1SJimmy Zuber 	snprintf(path, sizeof(path), "%s/file", self->dir);
284*60dbcce1SJimmy Zuber 	self->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
285*60dbcce1SJimmy Zuber 	ASSERT_GE(self->fd, 0);
286*60dbcce1SJimmy Zuber 	self->eof = eof;
287*60dbcce1SJimmy Zuber 
288*60dbcce1SJimmy Zuber 	buf = malloc(eof);
289*60dbcce1SJimmy Zuber 	ASSERT_NE(NULL, buf);
290*60dbcce1SJimmy Zuber 	memset(buf, 'A', eof);
291*60dbcce1SJimmy Zuber 	ASSERT_EQ(eof, pwrite(self->fd, buf, eof, 0));
292*60dbcce1SJimmy Zuber 	free(buf);
293*60dbcce1SJimmy Zuber 
294*60dbcce1SJimmy Zuber 	self->map = mmap(NULL, self->page, PROT_READ | PROT_WRITE, MAP_SHARED,
295*60dbcce1SJimmy Zuber 			 self->fd, eof & ~(self->page - 1));
296*60dbcce1SJimmy Zuber 	ASSERT_NE(MAP_FAILED, self->map);
297*60dbcce1SJimmy Zuber 	memset(self->map + (eof & (self->page - 1)), POLLUTE,
298*60dbcce1SJimmy Zuber 	       self->page - (eof & (self->page - 1)));
299*60dbcce1SJimmy Zuber }
300*60dbcce1SJimmy Zuber 
301*60dbcce1SJimmy Zuber /* Assert the old post-EOF tail [eof, end of its page) now reads back as zero. */
302*60dbcce1SJimmy Zuber static void assert_tail_zeroed(struct __test_metadata *_metadata,
303*60dbcce1SJimmy Zuber 			       FIXTURE_DATA(fuse) * self)
304*60dbcce1SJimmy Zuber {
305*60dbcce1SJimmy Zuber 	off_t base = self->eof & ~(self->page - 1);
306*60dbcce1SJimmy Zuber 	char *tail = malloc(self->page);
307*60dbcce1SJimmy Zuber 	int i;
308*60dbcce1SJimmy Zuber 
309*60dbcce1SJimmy Zuber 	ASSERT_NE(NULL, tail);
310*60dbcce1SJimmy Zuber 	ASSERT_EQ(self->page, pread(self->fd, tail, self->page, base));
311*60dbcce1SJimmy Zuber 	for (i = self->eof & (self->page - 1); i < self->page; i++)
312*60dbcce1SJimmy Zuber 		ASSERT_EQ(0, tail[i]);
313*60dbcce1SJimmy Zuber 	free(tail);
314*60dbcce1SJimmy Zuber }
315*60dbcce1SJimmy Zuber 
316*60dbcce1SJimmy Zuber /* Basic: pollute the post-EOF tail, extend past it by a later write. */
317*60dbcce1SJimmy Zuber TEST_F(fuse, write_extend)
318*60dbcce1SJimmy Zuber {
319*60dbcce1SJimmy Zuber 	pollute_eof_tail(_metadata, self);
320*60dbcce1SJimmy Zuber 	ASSERT_EQ(4, pwrite(self->fd, "data", 4, 5 * self->page + self->page / 3));
321*60dbcce1SJimmy Zuber 	assert_tail_zeroed(_metadata, self);
322*60dbcce1SJimmy Zuber }
323*60dbcce1SJimmy Zuber 
324*60dbcce1SJimmy Zuber /* Extend via ftruncate() rather than a write. */
325*60dbcce1SJimmy Zuber TEST_F(fuse, ftruncate_extend)
326*60dbcce1SJimmy Zuber {
327*60dbcce1SJimmy Zuber 	pollute_eof_tail(_metadata, self);
328*60dbcce1SJimmy Zuber 	ASSERT_EQ(0, ftruncate(self->fd, 8 * self->page));
329*60dbcce1SJimmy Zuber 	assert_tail_zeroed(_metadata, self);
330*60dbcce1SJimmy Zuber }
331*60dbcce1SJimmy Zuber 
332*60dbcce1SJimmy Zuber /* Extend via fallocate() starting at the old EOF. */
333*60dbcce1SJimmy Zuber TEST_F(fuse, fallocate_extend)
334*60dbcce1SJimmy Zuber {
335*60dbcce1SJimmy Zuber 	pollute_eof_tail(_metadata, self);
336*60dbcce1SJimmy Zuber 	ASSERT_EQ(0, fallocate(self->fd, 0, self->eof, 4 * self->page));
337*60dbcce1SJimmy Zuber 	assert_tail_zeroed(_metadata, self);
338*60dbcce1SJimmy Zuber }
339*60dbcce1SJimmy Zuber 
340*60dbcce1SJimmy Zuber /* A write landing inside the old EOF page must not clobber its own data. */
341*60dbcce1SJimmy Zuber TEST_F(fuse, extend_into_eof_page_preserves_data)
342*60dbcce1SJimmy Zuber {
343*60dbcce1SJimmy Zuber 	off_t base, wr;
344*60dbcce1SJimmy Zuber 	char *buf, *rd;
345*60dbcce1SJimmy Zuber 	int i;
346*60dbcce1SJimmy Zuber 
347*60dbcce1SJimmy Zuber 	pollute_eof_tail(_metadata, self);
348*60dbcce1SJimmy Zuber 	base = self->eof & ~(self->page - 1);
349*60dbcce1SJimmy Zuber 	wr = base + 3 * self->page / 4;		/* starts in the EOF page */
350*60dbcce1SJimmy Zuber 
351*60dbcce1SJimmy Zuber 	buf = malloc(2 * self->page);
352*60dbcce1SJimmy Zuber 	ASSERT_NE(NULL, buf);
353*60dbcce1SJimmy Zuber 	memset(buf, 'B', 2 * self->page);
354*60dbcce1SJimmy Zuber 	ASSERT_EQ(2 * self->page, pwrite(self->fd, buf, 2 * self->page, wr));
355*60dbcce1SJimmy Zuber 	free(buf);
356*60dbcce1SJimmy Zuber 
357*60dbcce1SJimmy Zuber 	rd = malloc(self->page);
358*60dbcce1SJimmy Zuber 	ASSERT_NE(NULL, rd);
359*60dbcce1SJimmy Zuber 	ASSERT_EQ(self->page, pread(self->fd, rd, self->page, base));
360*60dbcce1SJimmy Zuber 	/* [eof, wr) is hole -> zero; [wr, page) is written data -> 'B'. */
361*60dbcce1SJimmy Zuber 	for (i = self->eof & (self->page - 1); i < wr - base; i++)
362*60dbcce1SJimmy Zuber 		ASSERT_EQ(0, rd[i]);
363*60dbcce1SJimmy Zuber 	for (i = wr - base; i < self->page; i++)
364*60dbcce1SJimmy Zuber 		ASSERT_EQ('B', rd[i]);
365*60dbcce1SJimmy Zuber 	free(rd);
366*60dbcce1SJimmy Zuber }
367*60dbcce1SJimmy Zuber 
368*60dbcce1SJimmy Zuber TEST_HARNESS_MAIN
369