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