]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/write.cc
fusefs: commit missing file from r347547
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / write.cc
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 extern "C" {
32 #include <sys/types.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include <sys/sysctl.h>
36 #include <sys/uio.h>
37
38 #include <aio.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 }
42
43 #include "mockfs.hh"
44 #include "utils.hh"
45
46 using namespace testing;
47
48 class Write: public FuseTest {
49
50 public:
51
52 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
53 {
54         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
55 }
56
57 void expect_release(uint64_t ino, ProcessMockerT r)
58 {
59         EXPECT_CALL(*m_mock, process(
60                 ResultOf([=](auto in) {
61                         return (in->header.opcode == FUSE_RELEASE &&
62                                 in->header.nodeid == ino);
63                 }, Eq(true)),
64                 _)
65         ).WillRepeatedly(Invoke(r));
66 }
67
68 };
69
70 class AioWrite: public Write {
71 virtual void SetUp() {
72         const char *node = "vfs.aio.enable_unsafe";
73         int val = 0;
74         size_t size = sizeof(val);
75
76         FuseTest::SetUp();
77
78         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
79                 << strerror(errno);
80         if (!val)
81                 GTEST_SKIP() <<
82                         "vfs.aio.enable_unsafe must be set for this test";
83 }
84 };
85
86 /* Tests for the write-through cache mode */
87 class WriteThrough: public Write {
88
89 virtual void SetUp() {
90         const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
91         int val = 0;
92         size_t size = sizeof(val);
93
94         FuseTest::SetUp();
95         if (IsSkipped())
96                 return;
97
98         ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
99                 << strerror(errno);
100         if (val != 1)
101                 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
102                         "(writethrough) for this test";
103 }
104
105 };
106
107 /* Tests for the writeback cache mode */
108 class WriteBack: public Write {
109
110 virtual void SetUp() {
111         const char *node = "vfs.fusefs.data_cache_mode";
112         int val = 0;
113         size_t size = sizeof(val);
114
115         FuseTest::SetUp();
116         if (IsSkipped())
117                 return;
118
119         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
120                 << strerror(errno);
121         if (val != 2)
122                 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
123                         "(writeback) for this test";
124 }
125
126 };
127
128 /* AIO writes need to set the header's pid field correctly */
129 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
130 TEST_F(AioWrite, DISABLED_aio_write)
131 {
132         const char FULLPATH[] = "mountpoint/some_file.txt";
133         const char RELPATH[] = "some_file.txt";
134         const char *CONTENTS = "abcdefgh";
135         uint64_t ino = 42;
136         uint64_t offset = 4096;
137         int fd;
138         ssize_t bufsize = strlen(CONTENTS);
139         struct aiocb iocb, *piocb;
140
141         expect_lookup(RELPATH, ino, 0);
142         expect_open(ino, 0, 1);
143         expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
144
145         fd = open(FULLPATH, O_WRONLY);
146         EXPECT_LE(0, fd) << strerror(errno);
147
148         iocb.aio_nbytes = bufsize;
149         iocb.aio_fildes = fd;
150         iocb.aio_buf = (void *)CONTENTS;
151         iocb.aio_offset = offset;
152         iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
153         ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
154         ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
155         /* Deliberately leak fd.  close(2) will be tested in release.cc */
156 }
157
158 /* 
159  * When a file is opened with O_APPEND, we should forward that flag to
160  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
161  * offset internally.  That way we'll work both with filesystems that
162  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
163  * simply use the offset).
164  *
165  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
166  * Open.o_append test.
167  */
168 TEST_F(Write, append)
169 {
170         const ssize_t BUFSIZE = 9;
171         const char FULLPATH[] = "mountpoint/some_file.txt";
172         const char RELPATH[] = "some_file.txt";
173         const char CONTENTS[BUFSIZE] = "abcdefgh";
174         uint64_t ino = 42;
175         /* 
176          * Set offset to a maxbcachebuf boundary so we don't need to RMW when
177          * using writeback caching
178          */
179         uint64_t initial_offset = m_maxbcachebuf;
180         int fd;
181
182         expect_lookup(RELPATH, ino, initial_offset);
183         expect_open(ino, 0, 1);
184         expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
185
186         /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
187         fd = open(FULLPATH, O_RDWR | O_APPEND);
188         EXPECT_LE(0, fd) << strerror(errno);
189
190         ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
191         /* Deliberately leak fd.  close(2) will be tested in release.cc */
192 }
193
194 TEST_F(Write, append_direct_io)
195 {
196         const ssize_t BUFSIZE = 9;
197         const char FULLPATH[] = "mountpoint/some_file.txt";
198         const char RELPATH[] = "some_file.txt";
199         const char CONTENTS[BUFSIZE] = "abcdefgh";
200         uint64_t ino = 42;
201         uint64_t initial_offset = 4096;
202         int fd;
203
204         expect_lookup(RELPATH, ino, initial_offset);
205         expect_open(ino, FOPEN_DIRECT_IO, 1);
206         expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
207
208         fd = open(FULLPATH, O_WRONLY | O_APPEND);
209         EXPECT_LE(0, fd) << strerror(errno);
210
211         ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
212         /* Deliberately leak fd.  close(2) will be tested in release.cc */
213 }
214
215 /* A direct write should evict any overlapping cached data */
216 TEST_F(Write, direct_io_evicts_cache)
217 {
218         const char FULLPATH[] = "mountpoint/some_file.txt";
219         const char RELPATH[] = "some_file.txt";
220         const char CONTENTS0[] = "abcdefgh";
221         const char CONTENTS1[] = "ijklmnop";
222         uint64_t ino = 42;
223         int fd;
224         ssize_t bufsize = strlen(CONTENTS0) + 1;
225         char readbuf[bufsize];
226
227         expect_lookup(RELPATH, ino, bufsize);
228         expect_open(ino, 0, 1);
229         expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
230         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
231
232         fd = open(FULLPATH, O_RDWR);
233         EXPECT_LE(0, fd) << strerror(errno);
234
235         // Prime cache
236         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
237
238         // Write directly, evicting cache
239         ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
240         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
241         ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
242
243         // Read again.  Cache should be bypassed
244         expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
245         ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
246         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
247         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
248         ASSERT_STREQ(readbuf, CONTENTS1);
249
250         /* Deliberately leak fd.  close(2) will be tested in release.cc */
251 }
252
253 /*
254  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
255  * allowed to return a short write for that file handle.  However, if it does
256  * then we should still do our darndest to handle it by resending the unwritten
257  * portion.
258  */
259 TEST_F(Write, indirect_io_short_write)
260 {
261         const char FULLPATH[] = "mountpoint/some_file.txt";
262         const char RELPATH[] = "some_file.txt";
263         const char *CONTENTS = "abcdefghijklmnop";
264         uint64_t ino = 42;
265         int fd;
266         ssize_t bufsize = strlen(CONTENTS);
267         ssize_t bufsize0 = 11;
268         ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
269         const char *contents1 = CONTENTS + bufsize0;
270
271         expect_lookup(RELPATH, ino, 0);
272         expect_open(ino, 0, 1);
273         expect_write(ino, 0, bufsize, bufsize0, 0, CONTENTS);
274         expect_write(ino, bufsize0, bufsize1, bufsize1, 0,
275                 contents1);
276
277         fd = open(FULLPATH, O_WRONLY);
278         EXPECT_LE(0, fd) << strerror(errno);
279
280         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
281         /* Deliberately leak fd.  close(2) will be tested in release.cc */
282 }
283
284 /* 
285  * When the direct_io option is used, filesystems are allowed to write less
286  * data than requested.  We should return the short write to userland.
287  */
288 TEST_F(Write, direct_io_short_write)
289 {
290         const char FULLPATH[] = "mountpoint/some_file.txt";
291         const char RELPATH[] = "some_file.txt";
292         const char *CONTENTS = "abcdefghijklmnop";
293         uint64_t ino = 42;
294         int fd;
295         ssize_t bufsize = strlen(CONTENTS);
296         ssize_t halfbufsize = bufsize / 2;
297
298         expect_lookup(RELPATH, ino, 0);
299         expect_open(ino, FOPEN_DIRECT_IO, 1);
300         expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS);
301
302         fd = open(FULLPATH, O_WRONLY);
303         EXPECT_LE(0, fd) << strerror(errno);
304
305         ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
306         /* Deliberately leak fd.  close(2) will be tested in release.cc */
307 }
308
309 /*
310  * An insidious edge case: the filesystem returns a short write, and the
311  * difference between what we requested and what it actually wrote crosses an
312  * iov element boundary
313  */
314 TEST_F(Write, direct_io_short_write_iov)
315 {
316         const char FULLPATH[] = "mountpoint/some_file.txt";
317         const char RELPATH[] = "some_file.txt";
318         const char *CONTENTS0 = "abcdefgh";
319         const char *CONTENTS1 = "ijklmnop";
320         const char *EXPECTED0 = "abcdefghijklmnop";
321         uint64_t ino = 42;
322         int fd;
323         ssize_t size0 = strlen(CONTENTS0) - 1;
324         ssize_t size1 = strlen(CONTENTS1) + 1;
325         ssize_t totalsize = size0 + size1;
326         struct iovec iov[2];
327
328         expect_lookup(RELPATH, ino, 0);
329         expect_open(ino, FOPEN_DIRECT_IO, 1);
330         expect_write(ino, 0, totalsize, size0, 0, EXPECTED0);
331
332         fd = open(FULLPATH, O_WRONLY);
333         EXPECT_LE(0, fd) << strerror(errno);
334
335         iov[0].iov_base = (void*)CONTENTS0;
336         iov[0].iov_len = strlen(CONTENTS0);
337         iov[1].iov_base = (void*)CONTENTS1;
338         iov[1].iov_len = strlen(CONTENTS1);
339         ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
340         /* Deliberately leak fd.  close(2) will be tested in release.cc */
341 }
342
343 /*
344  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
345  * write, then it must set the FUSE_WRITE_CACHE bit
346  */
347 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
348 // TODO: check vfs.fusefs.mmap_enable
349 TEST_F(Write, mmap)
350 {
351         const char FULLPATH[] = "mountpoint/some_file.txt";
352         const char RELPATH[] = "some_file.txt";
353         const char *CONTENTS = "abcdefgh";
354         uint64_t ino = 42;
355         int fd;
356         ssize_t bufsize = strlen(CONTENTS);
357         void *p;
358         uint64_t offset = 10;
359         size_t len;
360         void *zeros, *expected;
361
362         len = getpagesize();
363
364         zeros = calloc(1, len);
365         ASSERT_NE(NULL, zeros);
366         expected = calloc(1, len);
367         ASSERT_NE(NULL, expected);
368         memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
369
370         expect_lookup(RELPATH, ino, len);
371         expect_open(ino, 0, 1);
372         expect_read(ino, 0, len, len, zeros);
373         /* 
374          * Writes from the pager may or may not be associated with the correct
375          * pid, so they must set FUSE_WRITE_CACHE.
376          * TODO: expect FUSE_WRITE_CACHE after upgrading to protocol 7.9
377          */
378         expect_write(ino, 0, len, len, 0, expected);
379         expect_flush(ino, 1, ReturnErrno(0));
380         expect_release(ino, ReturnErrno(0));
381
382         fd = open(FULLPATH, O_RDWR);
383         EXPECT_LE(0, fd) << strerror(errno);
384
385         p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
386         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
387
388         memmove((uint8_t*)p + offset, CONTENTS, bufsize);
389
390         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
391         close(fd);      // Write mmap'd data on close
392
393         free(expected);
394         free(zeros);
395 }
396
397 /* In WriteThrough mode, a write should evict overlapping cached data */
398 TEST_F(WriteThrough, evicts_read_cache)
399 {
400         const char FULLPATH[] = "mountpoint/some_file.txt";
401         const char RELPATH[] = "some_file.txt";
402         ssize_t bufsize = 65536;
403         /* End the write in the middle of a page */
404         ssize_t wrsize = bufsize - 1000;
405         char *contents0, *contents1, *readbuf, *expected;
406         uint64_t ino = 42;
407         int fd;
408
409         contents0 = (char*)malloc(bufsize);
410         memset(contents0, 'X', bufsize);
411         contents0[bufsize - 1] = '\0';  // Null-terminate
412         contents1 = (char*)malloc(wrsize);
413         memset(contents1, 'Y', wrsize);
414         readbuf = (char*)calloc(bufsize, 1);
415         expected = (char*)malloc(bufsize);
416         memset(expected, 'Y', wrsize);
417         memset(expected + wrsize, 'X', bufsize - wrsize);
418         expected[bufsize - 1] = '\0';   // Null-terminate
419
420         expect_lookup(RELPATH, ino, bufsize);
421         expect_open(ino, 0, 1);
422         expect_read(ino, 0, bufsize, bufsize, contents0);
423         expect_write(ino, 0, wrsize, wrsize, 0, contents1);
424
425         fd = open(FULLPATH, O_RDWR);
426         EXPECT_LE(0, fd) << strerror(errno);
427
428         // Prime cache
429         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
430
431         // Write directly, evicting cache
432         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
433         ASSERT_EQ(wrsize, write(fd, contents1, wrsize)) << strerror(errno);
434
435         // Read again.  Cache should be bypassed
436         expect_read(ino, 0, bufsize, bufsize, expected);
437         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
438         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
439         ASSERT_STREQ(readbuf, expected);
440
441         /* Deliberately leak fd.  close(2) will be tested in release.cc */
442 }
443
444 TEST_F(WriteThrough, pwrite)
445 {
446         const char FULLPATH[] = "mountpoint/some_file.txt";
447         const char RELPATH[] = "some_file.txt";
448         const char *CONTENTS = "abcdefgh";
449         uint64_t ino = 42;
450         uint64_t offset = 4096;
451         int fd;
452         ssize_t bufsize = strlen(CONTENTS);
453
454         expect_lookup(RELPATH, ino, 0);
455         expect_open(ino, 0, 1);
456         expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
457
458         fd = open(FULLPATH, O_WRONLY);
459         EXPECT_LE(0, fd) << strerror(errno);
460
461         ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
462                 << strerror(errno);
463         /* Deliberately leak fd.  close(2) will be tested in release.cc */
464 }
465
466 TEST_F(Write, write)
467 {
468         const char FULLPATH[] = "mountpoint/some_file.txt";
469         const char RELPATH[] = "some_file.txt";
470         const char *CONTENTS = "abcdefgh";
471         uint64_t ino = 42;
472         int fd;
473         ssize_t bufsize = strlen(CONTENTS);
474
475         expect_lookup(RELPATH, ino, 0);
476         expect_open(ino, 0, 1);
477         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
478
479         fd = open(FULLPATH, O_WRONLY);
480         EXPECT_LE(0, fd) << strerror(errno);
481
482         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
483         /* Deliberately leak fd.  close(2) will be tested in release.cc */
484 }
485
486 /* fuse(4) should not issue writes of greater size than the daemon requests */
487 TEST_F(Write, write_large)
488 {
489         const char FULLPATH[] = "mountpoint/some_file.txt";
490         const char RELPATH[] = "some_file.txt";
491         int *contents;
492         uint64_t ino = 42;
493         int fd;
494         ssize_t halfbufsize, bufsize;
495
496         halfbufsize = m_mock->m_max_write;
497         bufsize = halfbufsize * 2;
498         contents = (int*)malloc(bufsize);
499         ASSERT_NE(NULL, contents);
500         for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
501                 contents[i] = i;
502         }
503
504         expect_lookup(RELPATH, ino, 0);
505         expect_open(ino, 0, 1);
506         expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents);
507         expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
508                 &contents[halfbufsize / sizeof(int)]);
509
510         fd = open(FULLPATH, O_WRONLY);
511         EXPECT_LE(0, fd) << strerror(errno);
512
513         ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
514         /* Deliberately leak fd.  close(2) will be tested in release.cc */
515
516         free(contents);
517 }
518
519 TEST_F(Write, write_nothing)
520 {
521         const char FULLPATH[] = "mountpoint/some_file.txt";
522         const char RELPATH[] = "some_file.txt";
523         const char *CONTENTS = "";
524         uint64_t ino = 42;
525         int fd;
526         ssize_t bufsize = 0;
527
528         expect_lookup(RELPATH, ino, 0);
529         expect_open(ino, 0, 1);
530
531         fd = open(FULLPATH, O_WRONLY);
532         EXPECT_LE(0, fd) << strerror(errno);
533
534         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
535         /* Deliberately leak fd.  close(2) will be tested in release.cc */
536 }
537
538 /* In writeback mode, dirty data should be written on close */
539 TEST_F(WriteBack, close)
540 {
541         const char FULLPATH[] = "mountpoint/some_file.txt";
542         const char RELPATH[] = "some_file.txt";
543         const char *CONTENTS = "abcdefgh";
544         uint64_t ino = 42;
545         int fd;
546         ssize_t bufsize = strlen(CONTENTS);
547
548         expect_lookup(RELPATH, ino, 0);
549         expect_open(ino, 0, 1);
550         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
551         EXPECT_CALL(*m_mock, process(
552                 ResultOf([=](auto in) {
553                         return (in->header.opcode == FUSE_SETATTR);
554                 }, Eq(true)),
555                 _)
556         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
557                 SET_OUT_HEADER_LEN(out, attr);
558                 out->body.attr.attr.ino = ino;  // Must match nodeid
559         })));
560         expect_flush(ino, 1, ReturnErrno(0));
561         expect_release(ino, ReturnErrno(0));
562
563         fd = open(FULLPATH, O_RDWR);
564         ASSERT_LE(0, fd) << strerror(errno);
565
566         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
567         close(fd);
568 }
569
570 /*
571  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
572  * server.  The FUSE protocol explicitly allows that.
573  */
574 TEST_F(WriteBack, rmw)
575 {
576         const char FULLPATH[] = "mountpoint/some_file.txt";
577         const char RELPATH[] = "some_file.txt";
578         const char *CONTENTS = "abcdefgh";
579         const char *INITIAL   = "XXXXXXXXXX";
580         uint64_t ino = 42;
581         uint64_t offset = 1;
582         off_t fsize = 10;
583         int fd;
584         ssize_t bufsize = strlen(CONTENTS);
585
586         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
587         expect_open(ino, 0, 1);
588         expect_read(ino, 0, fsize, fsize, INITIAL);
589         expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
590
591         fd = open(FULLPATH, O_WRONLY);
592         EXPECT_LE(0, fd) << strerror(errno);
593
594         ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
595                 << strerror(errno);
596         /* Deliberately leak fd.  close(2) will be tested in release.cc */
597 }
598
599 /*
600  * Without direct_io, writes should be committed to cache
601  */
602 TEST_F(WriteBack, writeback)
603 {
604         const char FULLPATH[] = "mountpoint/some_file.txt";
605         const char RELPATH[] = "some_file.txt";
606         const char *CONTENTS = "abcdefgh";
607         uint64_t ino = 42;
608         int fd;
609         ssize_t bufsize = strlen(CONTENTS);
610         char readbuf[bufsize];
611
612         expect_lookup(RELPATH, ino, 0);
613         expect_open(ino, 0, 1);
614         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
615
616         fd = open(FULLPATH, O_RDWR);
617         EXPECT_LE(0, fd) << strerror(errno);
618
619         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
620         /* 
621          * A subsequent read should be serviced by cache, without querying the
622          * filesystem daemon
623          */
624         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
625         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
626         /* Deliberately leak fd.  close(2) will be tested in release.cc */
627 }
628
629 /*
630  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
631  * an odd test, because it would be unusual to use O_DIRECT for writes but not
632  * reads.
633  */
634 TEST_F(WriteBack, o_direct)
635 {
636         const char FULLPATH[] = "mountpoint/some_file.txt";
637         const char RELPATH[] = "some_file.txt";
638         const char *CONTENTS = "abcdefgh";
639         uint64_t ino = 42;
640         int fd;
641         ssize_t bufsize = strlen(CONTENTS);
642         char readbuf[bufsize];
643
644         expect_lookup(RELPATH, ino, 0);
645         expect_open(ino, 0, 1);
646         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
647         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
648
649         fd = open(FULLPATH, O_RDWR | O_DIRECT);
650         EXPECT_LE(0, fd) << strerror(errno);
651
652         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
653         /* A subsequent read must query the daemon because cache is empty */
654         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
655         ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
656         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
657         /* Deliberately leak fd.  close(2) will be tested in release.cc */
658 }
659
660 /*
661  * Without direct_io, writes should be committed to cache
662  */
663 /* 
664  * Disabled because we don't yet implement write-through caching.  No bugzilla
665  * entry, because that's a feature request, not a bug.
666  */
667 TEST_F(WriteThrough, DISABLED_writethrough)
668 {
669         const char FULLPATH[] = "mountpoint/some_file.txt";
670         const char RELPATH[] = "some_file.txt";
671         const char *CONTENTS = "abcdefgh";
672         uint64_t ino = 42;
673         int fd;
674         ssize_t bufsize = strlen(CONTENTS);
675         char readbuf[bufsize];
676
677         expect_lookup(RELPATH, ino, 0);
678         expect_open(ino, 0, 1);
679         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
680
681         fd = open(FULLPATH, O_RDWR);
682         EXPECT_LE(0, fd) << strerror(errno);
683
684         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
685         /* 
686          * A subsequent read should be serviced by cache, without querying the
687          * filesystem daemon
688          */
689         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
690         /* Deliberately leak fd.  close(2) will be tested in release.cc */
691 }
692
693 /* With writethrough caching, writes update the cached file size */
694 TEST_F(WriteThrough, update_file_size)
695 {
696         const char FULLPATH[] = "mountpoint/some_file.txt";
697         const char RELPATH[] = "some_file.txt";
698         const char *CONTENTS = "abcdefgh";
699         struct stat sb;
700         uint64_t ino = 42;
701         int fd;
702         ssize_t bufsize = strlen(CONTENTS);
703
704         expect_lookup(RELPATH, ino, 0);
705         expect_open(ino, 0, 1);
706         expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
707
708         fd = open(FULLPATH, O_RDWR);
709         EXPECT_LE(0, fd) << strerror(errno);
710
711         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
712         /* Get cached attributes */
713         ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
714         ASSERT_EQ(bufsize, sb.st_size);
715         /* Deliberately leak fd.  close(2) will be tested in release.cc */
716 }