]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/write.cc
fusefs: skip the Write.mmap test when mmap is not available
[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/param.h>
33 #include <sys/mman.h>
34 #include <sys/resource.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/time.h>
38 #include <sys/uio.h>
39
40 #include <aio.h>
41 #include <fcntl.h>
42 #include <signal.h>
43 #include <unistd.h>
44 }
45
46 #include "mockfs.hh"
47 #include "utils.hh"
48
49 using namespace testing;
50
51 class Write: public FuseTest {
52
53 public:
54 static sig_atomic_t s_sigxfsz;
55
56 void SetUp() {
57         s_sigxfsz = 0;
58         FuseTest::SetUp();
59 }
60
61 void TearDown() {
62         struct sigaction sa;
63
64         bzero(&sa, sizeof(sa));
65         sa.sa_handler = SIG_DFL;
66         sigaction(SIGXFSZ, &sa, NULL);
67
68         FuseTest::TearDown();
69 }
70
71 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
72 {
73         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
74 }
75
76 void expect_release(uint64_t ino, ProcessMockerT r)
77 {
78         EXPECT_CALL(*m_mock, process(
79                 ResultOf([=](auto in) {
80                         return (in.header.opcode == FUSE_RELEASE &&
81                                 in.header.nodeid == ino);
82                 }, Eq(true)),
83                 _)
84         ).WillRepeatedly(Invoke(r));
85 }
86
87 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
88         uint64_t osize, const void *contents)
89 {
90         FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
91 }
92
93 };
94
95 class WriteCacheable: public Write {
96 public:
97 virtual void SetUp() {
98         const char *node = "vfs.fusefs.data_cache_mode";
99         int val = 0;
100         size_t size = sizeof(val);
101
102         FuseTest::SetUp();
103
104         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
105                 << strerror(errno);
106         if (val == 0)
107                 GTEST_SKIP() <<
108                         "fusefs data caching must be enabled for this test";
109 }
110 };
111
112 sig_atomic_t Write::s_sigxfsz = 0;
113
114 class Write_7_8: public FuseTest {
115
116 public:
117 virtual void SetUp() {
118         m_kernel_minor_version = 8;
119         FuseTest::SetUp();
120 }
121
122 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
123 {
124         FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
125 }
126
127 };
128
129 class AioWrite: public Write {
130 virtual void SetUp() {
131         const char *node = "vfs.aio.enable_unsafe";
132         int val = 0;
133         size_t size = sizeof(val);
134
135         FuseTest::SetUp();
136
137         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
138                 << strerror(errno);
139         if (!val)
140                 GTEST_SKIP() <<
141                         "vfs.aio.enable_unsafe must be set for this test";
142 }
143 };
144
145 /* Tests for the write-through cache mode */
146 class WriteThrough: public Write {
147 public:
148 virtual void SetUp() {
149         const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
150         int val = 0;
151         size_t size = sizeof(val);
152
153         FuseTest::SetUp();
154         if (IsSkipped())
155                 return;
156
157         ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
158                 << strerror(errno);
159         if (val != 1)
160                 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
161                         "(writethrough) for this test";
162 }
163
164 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
165         uint64_t osize, const void *contents)
166 {
167         FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
168                 contents);
169 }
170 };
171
172 /* Tests for the writeback cache mode */
173 class WriteBack: public Write {
174 public:
175 virtual void SetUp() {
176         const char *node = "vfs.fusefs.data_cache_mode";
177         int val = 0;
178         size_t size = sizeof(val);
179
180         FuseTest::SetUp();
181         if (IsSkipped())
182                 return;
183
184         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
185                 << strerror(errno);
186         if (val != 2)
187                 GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
188                         "(writeback) for this test";
189 }
190
191 void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
192         uint64_t osize, const void *contents)
193 {
194         FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
195                 contents);
196 }
197 };
198
199 /* Tests for clustered writes with WriteBack cacheing */
200 class WriteCluster: public WriteBack {
201 public:
202 virtual void SetUp() {
203         if (MAXPHYS < 2 * DFLTPHYS)
204                 GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
205                         << "for this test";
206         m_async = true;
207         m_maxwrite = MAXPHYS;
208         WriteBack::SetUp();
209 }
210 };
211
212 void sigxfsz_handler(int __unused sig) {
213         Write::s_sigxfsz = 1;
214 }
215
216 /* AIO writes need to set the header's pid field correctly */
217 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
218 TEST_F(AioWrite, DISABLED_aio_write)
219 {
220         const char FULLPATH[] = "mountpoint/some_file.txt";
221         const char RELPATH[] = "some_file.txt";
222         const char *CONTENTS = "abcdefgh";
223         uint64_t ino = 42;
224         uint64_t offset = 4096;
225         int fd;
226         ssize_t bufsize = strlen(CONTENTS);
227         struct aiocb iocb, *piocb;
228
229         expect_lookup(RELPATH, ino, 0);
230         expect_open(ino, 0, 1);
231         expect_write(ino, offset, bufsize, bufsize, CONTENTS);
232
233         fd = open(FULLPATH, O_WRONLY);
234         EXPECT_LE(0, fd) << strerror(errno);
235
236         iocb.aio_nbytes = bufsize;
237         iocb.aio_fildes = fd;
238         iocb.aio_buf = (void *)CONTENTS;
239         iocb.aio_offset = offset;
240         iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
241         ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
242         ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
243         /* Deliberately leak fd.  close(2) will be tested in release.cc */
244 }
245
246 /* 
247  * When a file is opened with O_APPEND, we should forward that flag to
248  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
249  * offset internally.  That way we'll work both with filesystems that
250  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
251  * simply use the offset).
252  *
253  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
254  * Open.o_append test.
255  */
256 TEST_F(Write, append)
257 {
258         const ssize_t BUFSIZE = 9;
259         const char FULLPATH[] = "mountpoint/some_file.txt";
260         const char RELPATH[] = "some_file.txt";
261         const char CONTENTS[BUFSIZE] = "abcdefgh";
262         uint64_t ino = 42;
263         /* 
264          * Set offset to a maxbcachebuf boundary so we don't need to RMW when
265          * using writeback caching
266          */
267         uint64_t initial_offset = m_maxbcachebuf;
268         int fd;
269
270         expect_lookup(RELPATH, ino, initial_offset);
271         expect_open(ino, 0, 1);
272         expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
273
274         /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
275         fd = open(FULLPATH, O_RDWR | O_APPEND);
276         EXPECT_LE(0, fd) << strerror(errno);
277
278         ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
279         /* Deliberately leak fd.  close(2) will be tested in release.cc */
280 }
281
282 /* If a file is cached, then appending to the end should not cause a read */
283 TEST_F(Write, append_to_cached)
284 {
285         const ssize_t BUFSIZE = 9;
286         const char FULLPATH[] = "mountpoint/some_file.txt";
287         const char RELPATH[] = "some_file.txt";
288         char *oldcontents, *oldbuf;
289         const char CONTENTS[BUFSIZE] = "abcdefgh";
290         uint64_t ino = 42;
291         /* 
292          * Set offset in between maxbcachebuf boundary to test buffer handling
293          */
294         uint64_t oldsize = m_maxbcachebuf / 2;
295         int fd;
296
297         oldcontents = (char*)calloc(1, oldsize);
298         ASSERT_NE(NULL, oldcontents) << strerror(errno);
299         oldbuf = (char*)malloc(oldsize);
300         ASSERT_NE(NULL, oldbuf) << strerror(errno);
301
302         expect_lookup(RELPATH, ino, oldsize);
303         expect_open(ino, 0, 1);
304         expect_read(ino, 0, oldsize, oldsize, oldcontents);
305         expect_write(ino, oldsize, BUFSIZE, BUFSIZE, CONTENTS);
306
307         /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
308         fd = open(FULLPATH, O_RDWR | O_APPEND);
309         EXPECT_LE(0, fd) << strerror(errno);
310
311         /* Read the old data into the cache */
312         ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
313                 << strerror(errno);
314
315         /* Write the new data.  There should be no more read operations */
316         ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
317         /* Deliberately leak fd.  close(2) will be tested in release.cc */
318 }
319
320 TEST_F(Write, append_direct_io)
321 {
322         const ssize_t BUFSIZE = 9;
323         const char FULLPATH[] = "mountpoint/some_file.txt";
324         const char RELPATH[] = "some_file.txt";
325         const char CONTENTS[BUFSIZE] = "abcdefgh";
326         uint64_t ino = 42;
327         uint64_t initial_offset = 4096;
328         int fd;
329
330         expect_lookup(RELPATH, ino, initial_offset);
331         expect_open(ino, FOPEN_DIRECT_IO, 1);
332         expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
333
334         fd = open(FULLPATH, O_WRONLY | O_APPEND);
335         EXPECT_LE(0, fd) << strerror(errno);
336
337         ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
338         /* Deliberately leak fd.  close(2) will be tested in release.cc */
339 }
340
341 /* A direct write should evict any overlapping cached data */
342 TEST_F(Write, direct_io_evicts_cache)
343 {
344         const char FULLPATH[] = "mountpoint/some_file.txt";
345         const char RELPATH[] = "some_file.txt";
346         const char CONTENTS0[] = "abcdefgh";
347         const char CONTENTS1[] = "ijklmnop";
348         uint64_t ino = 42;
349         int fd;
350         ssize_t bufsize = strlen(CONTENTS0) + 1;
351         char readbuf[bufsize];
352
353         expect_lookup(RELPATH, ino, bufsize);
354         expect_open(ino, 0, 1);
355         expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
356         expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
357
358         fd = open(FULLPATH, O_RDWR);
359         EXPECT_LE(0, fd) << strerror(errno);
360
361         // Prime cache
362         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
363
364         // Write directly, evicting cache
365         ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
366         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
367         ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
368
369         // Read again.  Cache should be bypassed
370         expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
371         ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
372         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
373         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
374         ASSERT_STREQ(readbuf, CONTENTS1);
375
376         /* Deliberately leak fd.  close(2) will be tested in release.cc */
377 }
378
379 /*
380  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
381  * allowed to return a short write for that file handle.  However, if it does
382  * then we should still do our darndest to handle it by resending the unwritten
383  * portion.
384  */
385 TEST_F(Write, indirect_io_short_write)
386 {
387         const char FULLPATH[] = "mountpoint/some_file.txt";
388         const char RELPATH[] = "some_file.txt";
389         const char *CONTENTS = "abcdefghijklmnop";
390         uint64_t ino = 42;
391         int fd;
392         ssize_t bufsize = strlen(CONTENTS);
393         ssize_t bufsize0 = 11;
394         ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
395         const char *contents1 = CONTENTS + bufsize0;
396
397         expect_lookup(RELPATH, ino, 0);
398         expect_open(ino, 0, 1);
399         expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
400         expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
401
402         fd = open(FULLPATH, O_WRONLY);
403         EXPECT_LE(0, fd) << strerror(errno);
404
405         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
406         /* Deliberately leak fd.  close(2) will be tested in release.cc */
407 }
408
409 /* 
410  * When the direct_io option is used, filesystems are allowed to write less
411  * data than requested.  We should return the short write to userland.
412  */
413 TEST_F(Write, direct_io_short_write)
414 {
415         const char FULLPATH[] = "mountpoint/some_file.txt";
416         const char RELPATH[] = "some_file.txt";
417         const char *CONTENTS = "abcdefghijklmnop";
418         uint64_t ino = 42;
419         int fd;
420         ssize_t bufsize = strlen(CONTENTS);
421         ssize_t halfbufsize = bufsize / 2;
422
423         expect_lookup(RELPATH, ino, 0);
424         expect_open(ino, FOPEN_DIRECT_IO, 1);
425         expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
426
427         fd = open(FULLPATH, O_WRONLY);
428         EXPECT_LE(0, fd) << strerror(errno);
429
430         ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
431         /* Deliberately leak fd.  close(2) will be tested in release.cc */
432 }
433
434 /*
435  * An insidious edge case: the filesystem returns a short write, and the
436  * difference between what we requested and what it actually wrote crosses an
437  * iov element boundary
438  */
439 TEST_F(Write, direct_io_short_write_iov)
440 {
441         const char FULLPATH[] = "mountpoint/some_file.txt";
442         const char RELPATH[] = "some_file.txt";
443         const char *CONTENTS0 = "abcdefgh";
444         const char *CONTENTS1 = "ijklmnop";
445         const char *EXPECTED0 = "abcdefghijklmnop";
446         uint64_t ino = 42;
447         int fd;
448         ssize_t size0 = strlen(CONTENTS0) - 1;
449         ssize_t size1 = strlen(CONTENTS1) + 1;
450         ssize_t totalsize = size0 + size1;
451         struct iovec iov[2];
452
453         expect_lookup(RELPATH, ino, 0);
454         expect_open(ino, FOPEN_DIRECT_IO, 1);
455         expect_write(ino, 0, totalsize, size0, EXPECTED0);
456
457         fd = open(FULLPATH, O_WRONLY);
458         EXPECT_LE(0, fd) << strerror(errno);
459
460         iov[0].iov_base = (void*)CONTENTS0;
461         iov[0].iov_len = strlen(CONTENTS0);
462         iov[1].iov_base = (void*)CONTENTS1;
463         iov[1].iov_len = strlen(CONTENTS1);
464         ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
465         /* Deliberately leak fd.  close(2) will be tested in release.cc */
466 }
467
468 /* fusefs should respect RLIMIT_FSIZE */
469 TEST_F(Write, rlimit_fsize)
470 {
471         const char FULLPATH[] = "mountpoint/some_file.txt";
472         const char RELPATH[] = "some_file.txt";
473         const char *CONTENTS = "abcdefgh";
474         struct rlimit rl;
475         ssize_t bufsize = strlen(CONTENTS);
476         off_t offset = 1'000'000'000;
477         uint64_t ino = 42;
478         int fd;
479
480         expect_lookup(RELPATH, ino, 0);
481         expect_open(ino, 0, 1);
482
483         rl.rlim_cur = offset;
484         rl.rlim_max = 10 * offset;
485         ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
486         ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
487
488         fd = open(FULLPATH, O_WRONLY);
489
490         EXPECT_LE(0, fd) << strerror(errno);
491
492         ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
493         EXPECT_EQ(EFBIG, errno);
494         EXPECT_EQ(1, s_sigxfsz);
495         /* Deliberately leak fd.  close(2) will be tested in release.cc */
496 }
497
498 /*
499  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
500  * write, then it must set the FUSE_WRITE_CACHE bit
501  */
502 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
503 TEST_F(WriteCacheable, mmap)
504 {
505         const char FULLPATH[] = "mountpoint/some_file.txt";
506         const char RELPATH[] = "some_file.txt";
507         const char *CONTENTS = "abcdefgh";
508         uint64_t ino = 42;
509         int fd;
510         ssize_t bufsize = strlen(CONTENTS);
511         void *p;
512         uint64_t offset = 10;
513         size_t len;
514         void *zeros, *expected;
515
516         len = getpagesize();
517
518         zeros = calloc(1, len);
519         ASSERT_NE(NULL, zeros);
520         expected = calloc(1, len);
521         ASSERT_NE(NULL, expected);
522         memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
523
524         expect_lookup(RELPATH, ino, len);
525         expect_open(ino, 0, 1);
526         expect_read(ino, 0, len, len, zeros);
527         /* 
528          * Writes from the pager may or may not be associated with the correct
529          * pid, so they must set FUSE_WRITE_CACHE.
530          */
531         FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
532         expect_flush(ino, 1, ReturnErrno(0));
533         expect_release(ino, ReturnErrno(0));
534
535         fd = open(FULLPATH, O_RDWR);
536         EXPECT_LE(0, fd) << strerror(errno);
537
538         p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
539         ASSERT_NE(MAP_FAILED, p) << strerror(errno);
540
541         memmove((uint8_t*)p + offset, CONTENTS, bufsize);
542
543         ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
544         close(fd);      // Write mmap'd data on close
545
546         free(expected);
547         free(zeros);
548 }
549
550 TEST_F(WriteThrough, pwrite)
551 {
552         const char FULLPATH[] = "mountpoint/some_file.txt";
553         const char RELPATH[] = "some_file.txt";
554         const char *CONTENTS = "abcdefgh";
555         uint64_t ino = 42;
556         uint64_t offset = 65536;
557         int fd;
558         ssize_t bufsize = strlen(CONTENTS);
559
560         expect_lookup(RELPATH, ino, 0);
561         expect_open(ino, 0, 1);
562         expect_write(ino, offset, bufsize, bufsize, CONTENTS);
563
564         fd = open(FULLPATH, O_WRONLY);
565         EXPECT_LE(0, fd) << strerror(errno);
566
567         ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
568                 << strerror(errno);
569         /* Deliberately leak fd.  close(2) will be tested in release.cc */
570 }
571
572 TEST_F(Write, write)
573 {
574         const char FULLPATH[] = "mountpoint/some_file.txt";
575         const char RELPATH[] = "some_file.txt";
576         const char *CONTENTS = "abcdefgh";
577         uint64_t ino = 42;
578         int fd;
579         ssize_t bufsize = strlen(CONTENTS);
580
581         expect_lookup(RELPATH, ino, 0);
582         expect_open(ino, 0, 1);
583         expect_write(ino, 0, bufsize, bufsize, CONTENTS);
584
585         fd = open(FULLPATH, O_WRONLY);
586         EXPECT_LE(0, fd) << strerror(errno);
587
588         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
589         /* Deliberately leak fd.  close(2) will be tested in release.cc */
590 }
591
592 /* fuse(4) should not issue writes of greater size than the daemon requests */
593 TEST_F(Write, write_large)
594 {
595         const char FULLPATH[] = "mountpoint/some_file.txt";
596         const char RELPATH[] = "some_file.txt";
597         int *contents;
598         uint64_t ino = 42;
599         int fd;
600         ssize_t halfbufsize, bufsize;
601
602         halfbufsize = m_mock->m_maxwrite;
603         bufsize = halfbufsize * 2;
604         contents = (int*)malloc(bufsize);
605         ASSERT_NE(NULL, contents);
606         for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
607                 contents[i] = i;
608         }
609
610         expect_lookup(RELPATH, ino, 0);
611         expect_open(ino, 0, 1);
612         expect_write(ino, 0, halfbufsize, halfbufsize, contents);
613         expect_write(ino, halfbufsize, halfbufsize, halfbufsize,
614                 &contents[halfbufsize / sizeof(int)]);
615
616         fd = open(FULLPATH, O_WRONLY);
617         EXPECT_LE(0, fd) << strerror(errno);
618
619         ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
620         /* Deliberately leak fd.  close(2) will be tested in release.cc */
621
622         free(contents);
623 }
624
625 TEST_F(Write, write_nothing)
626 {
627         const char FULLPATH[] = "mountpoint/some_file.txt";
628         const char RELPATH[] = "some_file.txt";
629         const char *CONTENTS = "";
630         uint64_t ino = 42;
631         int fd;
632         ssize_t bufsize = 0;
633
634         expect_lookup(RELPATH, ino, 0);
635         expect_open(ino, 0, 1);
636
637         fd = open(FULLPATH, O_WRONLY);
638         EXPECT_LE(0, fd) << strerror(errno);
639
640         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
641         /* Deliberately leak fd.  close(2) will be tested in release.cc */
642 }
643
644 TEST_F(Write_7_8, write)
645 {
646         const char FULLPATH[] = "mountpoint/some_file.txt";
647         const char RELPATH[] = "some_file.txt";
648         const char *CONTENTS = "abcdefgh";
649         uint64_t ino = 42;
650         int fd;
651         ssize_t bufsize = strlen(CONTENTS);
652
653         expect_lookup(RELPATH, ino, 0);
654         expect_open(ino, 0, 1);
655         expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
656
657         fd = open(FULLPATH, O_WRONLY);
658         EXPECT_LE(0, fd) << strerror(errno);
659
660         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
661         /* Deliberately leak fd.  close(2) will be tested in release.cc */
662 }
663
664 /* In writeback mode, dirty data should be written on close */
665 TEST_F(WriteBack, close)
666 {
667         const char FULLPATH[] = "mountpoint/some_file.txt";
668         const char RELPATH[] = "some_file.txt";
669         const char *CONTENTS = "abcdefgh";
670         uint64_t ino = 42;
671         int fd;
672         ssize_t bufsize = strlen(CONTENTS);
673
674         expect_lookup(RELPATH, ino, 0);
675         expect_open(ino, 0, 1);
676         expect_write(ino, 0, bufsize, bufsize, CONTENTS);
677         EXPECT_CALL(*m_mock, process(
678                 ResultOf([=](auto in) {
679                         return (in.header.opcode == FUSE_SETATTR);
680                 }, Eq(true)),
681                 _)
682         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
683                 SET_OUT_HEADER_LEN(out, attr);
684                 out.body.attr.attr.ino = ino;   // Must match nodeid
685         })));
686         expect_flush(ino, 1, ReturnErrno(0));
687         expect_release(ino, ReturnErrno(0));
688
689         fd = open(FULLPATH, O_RDWR);
690         ASSERT_LE(0, fd) << strerror(errno);
691
692         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
693         close(fd);
694 }
695
696 /* In writeback mode, adjacent writes will be clustered together */
697 TEST_F(WriteCluster, clustering)
698 {
699         const char FULLPATH[] = "mountpoint/some_file.txt";
700         const char RELPATH[] = "some_file.txt";
701         uint64_t ino = 42;
702         int i, fd;
703         void *wbuf, *wbuf2x;
704         ssize_t bufsize = 65536;
705         off_t filesize = 327680;
706
707         wbuf = malloc(bufsize);
708         ASSERT_NE(NULL, wbuf) << strerror(errno);
709         memset(wbuf, 'X', bufsize);
710         wbuf2x = malloc(2 * bufsize);
711         ASSERT_NE(NULL, wbuf2x) << strerror(errno);
712         memset(wbuf2x, 'X', 2 * bufsize);
713
714         expect_lookup(RELPATH, ino, filesize);
715         expect_open(ino, 0, 1);
716         /*
717          * Writes of bufsize-bytes each should be clustered into greater sizes.
718          * The amount of clustering is adaptive, so the first write actually
719          * issued will be 2x bufsize and subsequent writes may be larger
720          */
721         expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
722         expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
723         expect_flush(ino, 1, ReturnErrno(0));
724         expect_release(ino, ReturnErrno(0));
725
726         fd = open(FULLPATH, O_RDWR);
727         ASSERT_LE(0, fd) << strerror(errno);
728
729         for (i = 0; i < 4; i++) {
730                 ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
731                         << strerror(errno);
732         }
733         close(fd);
734 }
735
736 /* 
737  * When clustering writes, an I/O error to any of the cluster's children should
738  * not panic the system on unmount
739  */
740 /*
741  * Disabled because it panics.
742  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
743  */
744 TEST_F(WriteCluster, DISABLED_cluster_write_err)
745 {
746         const char FULLPATH[] = "mountpoint/some_file.txt";
747         const char RELPATH[] = "some_file.txt";
748         uint64_t ino = 42;
749         int i, fd;
750         void *wbuf;
751         ssize_t bufsize = 65536;
752         off_t filesize = 262144;
753
754         wbuf = malloc(bufsize);
755         ASSERT_NE(NULL, wbuf) << strerror(errno);
756         memset(wbuf, 'X', bufsize);
757
758         expect_lookup(RELPATH, ino, filesize);
759         expect_open(ino, 0, 1);
760         EXPECT_CALL(*m_mock, process(
761                 ResultOf([=](auto in) {
762                         return (in.header.opcode == FUSE_WRITE);
763                 }, Eq(true)),
764                 _)
765         ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
766         expect_flush(ino, 1, ReturnErrno(0));
767         expect_release(ino, ReturnErrno(0));
768
769         fd = open(FULLPATH, O_RDWR);
770         ASSERT_LE(0, fd) << strerror(errno);
771
772         for (i = 0; i < 3; i++) {
773                 ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
774                         << strerror(errno);
775         }
776         close(fd);
777 }
778
779 /*
780  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
781  * server.  The FUSE protocol explicitly allows that.
782  */
783 TEST_F(WriteBack, rmw)
784 {
785         const char FULLPATH[] = "mountpoint/some_file.txt";
786         const char RELPATH[] = "some_file.txt";
787         const char *CONTENTS = "abcdefgh";
788         const char *INITIAL   = "XXXXXXXXXX";
789         uint64_t ino = 42;
790         uint64_t offset = 1;
791         off_t fsize = 10;
792         int fd;
793         ssize_t bufsize = strlen(CONTENTS);
794
795         FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
796         expect_open(ino, 0, 1);
797         expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
798         expect_write(ino, offset, bufsize, bufsize, CONTENTS);
799
800         fd = open(FULLPATH, O_WRONLY);
801         EXPECT_LE(0, fd) << strerror(errno);
802
803         ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
804                 << strerror(errno);
805         /* Deliberately leak fd.  close(2) will be tested in release.cc */
806 }
807
808 /*
809  * Without direct_io, writes should be committed to cache
810  */
811 TEST_F(WriteBack, writeback)
812 {
813         const char FULLPATH[] = "mountpoint/some_file.txt";
814         const char RELPATH[] = "some_file.txt";
815         const char *CONTENTS = "abcdefgh";
816         uint64_t ino = 42;
817         int fd;
818         ssize_t bufsize = strlen(CONTENTS);
819         char readbuf[bufsize];
820
821         expect_lookup(RELPATH, ino, 0);
822         expect_open(ino, 0, 1);
823         expect_write(ino, 0, bufsize, bufsize, CONTENTS);
824
825         fd = open(FULLPATH, O_RDWR);
826         EXPECT_LE(0, fd) << strerror(errno);
827
828         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
829         /* 
830          * A subsequent read should be serviced by cache, without querying the
831          * filesystem daemon
832          */
833         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
834         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
835         /* Deliberately leak fd.  close(2) will be tested in release.cc */
836 }
837
838 /*
839  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
840  * an odd test, because it would be unusual to use O_DIRECT for writes but not
841  * reads.
842  */
843 TEST_F(WriteBack, o_direct)
844 {
845         const char FULLPATH[] = "mountpoint/some_file.txt";
846         const char RELPATH[] = "some_file.txt";
847         const char *CONTENTS = "abcdefgh";
848         uint64_t ino = 42;
849         int fd;
850         ssize_t bufsize = strlen(CONTENTS);
851         char readbuf[bufsize];
852
853         expect_lookup(RELPATH, ino, 0);
854         expect_open(ino, 0, 1);
855         FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
856                 CONTENTS);
857         expect_read(ino, 0, bufsize, bufsize, CONTENTS);
858
859         fd = open(FULLPATH, O_RDWR | O_DIRECT);
860         EXPECT_LE(0, fd) << strerror(errno);
861
862         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
863         /* A subsequent read must query the daemon because cache is empty */
864         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
865         ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
866         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
867         /* Deliberately leak fd.  close(2) will be tested in release.cc */
868 }
869
870 /*
871  * Without direct_io, writes should be committed to cache
872  */
873 TEST_F(WriteThrough, writethrough)
874 {
875         const char FULLPATH[] = "mountpoint/some_file.txt";
876         const char RELPATH[] = "some_file.txt";
877         const char *CONTENTS = "abcdefgh";
878         uint64_t ino = 42;
879         int fd;
880         ssize_t bufsize = strlen(CONTENTS);
881         char readbuf[bufsize];
882
883         expect_lookup(RELPATH, ino, 0);
884         expect_open(ino, 0, 1);
885         expect_write(ino, 0, bufsize, bufsize, CONTENTS);
886
887         fd = open(FULLPATH, O_RDWR);
888         EXPECT_LE(0, fd) << strerror(errno);
889
890         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
891         /*
892          * A subsequent read should be serviced by cache, without querying the
893          * filesystem daemon
894          */
895         ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
896         ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
897         /* Deliberately leak fd.  close(2) will be tested in release.cc */
898 }
899
900 /* With writethrough caching, writes update the cached file size */
901 TEST_F(WriteThrough, update_file_size)
902 {
903         const char FULLPATH[] = "mountpoint/some_file.txt";
904         const char RELPATH[] = "some_file.txt";
905         const char *CONTENTS = "abcdefgh";
906         struct stat sb;
907         uint64_t ino = 42;
908         int fd;
909         ssize_t bufsize = strlen(CONTENTS);
910
911         expect_lookup(RELPATH, ino, 0);
912         expect_open(ino, 0, 1);
913         expect_write(ino, 0, bufsize, bufsize, CONTENTS);
914
915         fd = open(FULLPATH, O_RDWR);
916         EXPECT_LE(0, fd) << strerror(errno);
917
918         ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
919         /* Get cached attributes */
920         ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
921         ASSERT_EQ(bufsize, sb.st_size);
922         /* Deliberately leak fd.  close(2) will be tested in release.cc */
923 }