Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/mockfs.cc
105177 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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
34
#include <sys/mount.h>
35
#include <sys/select.h>
36
#include <sys/stat.h>
37
#include <sys/uio.h>
38
#include <sys/user.h>
39
40
#include <fcntl.h>
41
#include <libutil.h>
42
#include <mntopts.h> // for build_iovec
43
#include <poll.h>
44
#include <pthread.h>
45
#include <signal.h>
46
#include <stdlib.h>
47
#include <unistd.h>
48
}
49
50
#include <cinttypes>
51
52
#include <gtest/gtest.h>
53
54
#include "mockfs.hh"
55
56
using namespace testing;
57
58
int verbosity = 0;
59
60
const char* opcode2opname(uint32_t opcode)
61
{
62
const char* table[] = {
63
"Unknown (opcode 0)",
64
"LOOKUP",
65
"FORGET",
66
"GETATTR",
67
"SETATTR",
68
"READLINK",
69
"SYMLINK",
70
"Unknown (opcode 7)",
71
"MKNOD",
72
"MKDIR",
73
"UNLINK",
74
"RMDIR",
75
"RENAME",
76
"LINK",
77
"OPEN",
78
"READ",
79
"WRITE",
80
"STATFS",
81
"RELEASE",
82
"Unknown (opcode 19)",
83
"FSYNC",
84
"SETXATTR",
85
"GETXATTR",
86
"LISTXATTR",
87
"REMOVEXATTR",
88
"FLUSH",
89
"INIT",
90
"OPENDIR",
91
"READDIR",
92
"RELEASEDIR",
93
"FSYNCDIR",
94
"GETLK",
95
"SETLK",
96
"SETLKW",
97
"ACCESS",
98
"CREATE",
99
"INTERRUPT",
100
"BMAP",
101
"DESTROY",
102
"IOCTL",
103
"POLL",
104
"NOTIFY_REPLY",
105
"BATCH_FORGET",
106
"FALLOCATE",
107
"READDIRPLUS",
108
"RENAME2",
109
"LSEEK",
110
"COPY_FILE_RANGE",
111
};
112
if (opcode >= nitems(table))
113
return ("Unknown (opcode > max)");
114
else
115
return (table[opcode]);
116
}
117
118
ProcessMockerT
119
ReturnErrno(int error)
120
{
121
return([=](auto in, auto &out) {
122
std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
123
out0->header.unique = in.header.unique;
124
out0->header.error = -error;
125
out0->header.len = sizeof(out0->header);
126
out.push_back(std::move(out0));
127
});
128
}
129
130
/* Helper function used for returning negative cache entries for LOOKUP */
131
ProcessMockerT
132
ReturnNegativeCache(const struct timespec *entry_valid)
133
{
134
return([=](auto in, auto &out) {
135
/* nodeid means ENOENT and cache it */
136
std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
137
out0->body.entry.nodeid = 0;
138
out0->header.unique = in.header.unique;
139
out0->header.error = 0;
140
out0->body.entry.entry_valid = entry_valid->tv_sec;
141
out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
142
SET_OUT_HEADER_LEN(*out0, entry);
143
out.push_back(std::move(out0));
144
});
145
}
146
147
ProcessMockerT
148
ReturnImmediate(std::function<void(const mockfs_buf_in& in,
149
struct mockfs_buf_out &out)> f)
150
{
151
return([=](auto& in, auto &out) {
152
std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
153
out0->header.unique = in.header.unique;
154
f(in, *out0);
155
out.push_back(std::move(out0));
156
});
157
}
158
159
void sigint_handler(int __unused sig) {
160
// Don't do anything except interrupt the daemon's read(2) call
161
}
162
163
void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
164
{
165
printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
166
in.header.nodeid);
167
if (verbosity > 1) {
168
printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
169
" buflen=%zd",
170
in.header.uid, in.header.gid, in.header.pid,
171
in.header.unique, in.header.len, buflen);
172
}
173
switch (in.header.opcode) {
174
const char *name, *value;
175
176
case FUSE_ACCESS:
177
printf(" mask=%#x", in.body.access.mask);
178
break;
179
case FUSE_BMAP:
180
printf(" block=%" PRIx64 " blocksize=%#x",
181
in.body.bmap.block, in.body.bmap.blocksize);
182
break;
183
case FUSE_COPY_FILE_RANGE:
184
printf(" off_in=%" PRIu64 " ino_out=%" PRIu64
185
" off_out=%" PRIu64 " size=%" PRIu64,
186
in.body.copy_file_range.off_in,
187
in.body.copy_file_range.nodeid_out,
188
in.body.copy_file_range.off_out,
189
in.body.copy_file_range.len);
190
if (verbosity > 1)
191
printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64
192
" flags=%" PRIx64,
193
in.body.copy_file_range.fh_in,
194
in.body.copy_file_range.fh_out,
195
in.body.copy_file_range.flags);
196
break;
197
case FUSE_CREATE:
198
if (m_kernel_minor_version >= 12)
199
name = (const char*)in.body.bytes +
200
sizeof(fuse_create_in);
201
else
202
name = (const char*)in.body.bytes +
203
sizeof(fuse_open_in);
204
printf(" flags=%#x name=%s",
205
in.body.open.flags, name);
206
break;
207
case FUSE_FALLOCATE:
208
printf(" fh=%#" PRIx64 " offset=%" PRIu64
209
" length=%" PRIx64 " mode=%#x",
210
in.body.fallocate.fh,
211
in.body.fallocate.offset,
212
in.body.fallocate.length,
213
in.body.fallocate.mode);
214
break;
215
case FUSE_FLUSH:
216
printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
217
in.body.flush.fh,
218
in.body.flush.lock_owner);
219
break;
220
case FUSE_FORGET:
221
printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
222
break;
223
case FUSE_FSYNC:
224
printf(" flags=%#x", in.body.fsync.fsync_flags);
225
break;
226
case FUSE_FSYNCDIR:
227
printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
228
break;
229
case FUSE_GETLK:
230
printf(" fh=%#" PRIx64
231
" type=%u pid=%u",
232
in.body.getlk.fh,
233
in.body.getlk.lk.type,
234
in.body.getlk.lk.pid);
235
if (verbosity >= 2) {
236
printf(" range=[%" PRIi64 ":%" PRIi64 "]",
237
in.body.getlk.lk.start,
238
in.body.getlk.lk.end);
239
}
240
break;
241
case FUSE_INTERRUPT:
242
printf(" unique=%" PRIu64, in.body.interrupt.unique);
243
break;
244
case FUSE_IOCTL:
245
printf(" flags=%#x cmd=%#x in_size=%" PRIu32
246
" out_size=%" PRIu32,
247
in.body.ioctl.flags, in.body.ioctl.cmd,
248
in.body.ioctl.in_size, in.body.ioctl.out_size);
249
break;
250
case FUSE_LINK:
251
printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
252
break;
253
case FUSE_LISTXATTR:
254
printf(" size=%" PRIu32, in.body.listxattr.size);
255
break;
256
case FUSE_LOOKUP:
257
printf(" %s", in.body.lookup);
258
break;
259
case FUSE_LSEEK:
260
switch (in.body.lseek.whence) {
261
case SEEK_HOLE:
262
printf(" SEEK_HOLE offset=%jd",
263
in.body.lseek.offset);
264
break;
265
case SEEK_DATA:
266
printf(" SEEK_DATA offset=%jd",
267
in.body.lseek.offset);
268
break;
269
default:
270
printf(" whence=%u offset=%jd",
271
in.body.lseek.whence, in.body.lseek.offset);
272
break;
273
}
274
break;
275
case FUSE_MKDIR:
276
name = (const char*)in.body.bytes +
277
sizeof(fuse_mkdir_in);
278
printf(" name=%s mode=%#o umask=%#o", name,
279
in.body.mkdir.mode, in.body.mkdir.umask);
280
break;
281
case FUSE_MKNOD:
282
if (m_kernel_minor_version >= 12)
283
name = (const char*)in.body.bytes +
284
sizeof(fuse_mknod_in);
285
else
286
name = (const char*)in.body.bytes +
287
FUSE_COMPAT_MKNOD_IN_SIZE;
288
printf(" mode=%#o rdev=%x umask=%#o name=%s",
289
in.body.mknod.mode, in.body.mknod.rdev,
290
in.body.mknod.umask, name);
291
break;
292
case FUSE_OPEN:
293
printf(" flags=%#x", in.body.open.flags);
294
break;
295
case FUSE_OPENDIR:
296
printf(" flags=%#x", in.body.opendir.flags);
297
break;
298
case FUSE_READ:
299
printf(" offset=%" PRIu64 " size=%u",
300
in.body.read.offset,
301
in.body.read.size);
302
if (verbosity > 1)
303
printf(" flags=%#x", in.body.read.flags);
304
break;
305
case FUSE_READDIR:
306
printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
307
in.body.readdir.fh, in.body.readdir.offset,
308
in.body.readdir.size);
309
break;
310
case FUSE_RELEASE:
311
printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
312
in.body.release.fh,
313
in.body.release.flags,
314
in.body.release.lock_owner);
315
break;
316
case FUSE_RENAME:
317
{
318
const char *src = (const char*)in.body.bytes +
319
sizeof(fuse_rename_in);
320
const char *dst = src + strlen(src) + 1;
321
printf(" src=%s newdir=%" PRIu64 " dst=%s",
322
src, in.body.rename.newdir, dst);
323
}
324
break;
325
case FUSE_SETATTR:
326
if (verbosity <= 1) {
327
printf(" valid=%#x", in.body.setattr.valid);
328
break;
329
}
330
if (in.body.setattr.valid & FATTR_MODE)
331
printf(" mode=%#o", in.body.setattr.mode);
332
if (in.body.setattr.valid & FATTR_UID)
333
printf(" uid=%u", in.body.setattr.uid);
334
if (in.body.setattr.valid & FATTR_GID)
335
printf(" gid=%u", in.body.setattr.gid);
336
if (in.body.setattr.valid & FATTR_SIZE)
337
printf(" size=%" PRIu64, in.body.setattr.size);
338
if (in.body.setattr.valid & FATTR_ATIME)
339
printf(" atime=%" PRIu64 ".%u",
340
in.body.setattr.atime,
341
in.body.setattr.atimensec);
342
if (in.body.setattr.valid & FATTR_MTIME)
343
printf(" mtime=%" PRIu64 ".%u",
344
in.body.setattr.mtime,
345
in.body.setattr.mtimensec);
346
if (in.body.setattr.valid & FATTR_FH)
347
printf(" fh=%" PRIu64 "", in.body.setattr.fh);
348
break;
349
case FUSE_SETLK:
350
printf(" fh=%#" PRIx64 " owner=%" PRIu64
351
" type=%u pid=%u",
352
in.body.setlk.fh, in.body.setlk.owner,
353
in.body.setlk.lk.type,
354
in.body.setlk.lk.pid);
355
if (verbosity >= 2) {
356
printf(" range=[%" PRIi64 ":%" PRIi64 "]",
357
in.body.setlk.lk.start,
358
in.body.setlk.lk.end);
359
}
360
break;
361
case FUSE_SETXATTR:
362
/*
363
* In theory neither the xattr name and value need be
364
* ASCII, but in this test suite they always are.
365
*/
366
name = (const char*)in.body.bytes +
367
sizeof(fuse_setxattr_in);
368
value = name + strlen(name) + 1;
369
printf(" %s=%s", name, value);
370
break;
371
case FUSE_WRITE:
372
printf(" fh=%#" PRIx64 " offset=%" PRIu64
373
" size=%u write_flags=%u",
374
in.body.write.fh,
375
in.body.write.offset, in.body.write.size,
376
in.body.write.write_flags);
377
if (verbosity > 1)
378
printf(" flags=%#x", in.body.write.flags);
379
break;
380
default:
381
break;
382
}
383
printf("\n");
384
}
385
386
/*
387
* Debug a FUSE response.
388
*
389
* This is mostly useful for asynchronous notifications, which don't correspond
390
* to any request
391
*/
392
void MockFS::debug_response(const mockfs_buf_out &out) {
393
const char *name;
394
395
if (verbosity == 0)
396
return;
397
398
switch (out.header.error) {
399
case FUSE_NOTIFY_INVAL_ENTRY:
400
name = (const char*)out.body.bytes +
401
sizeof(fuse_notify_inval_entry_out);
402
printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
403
out.body.inval_entry.parent, name);
404
break;
405
case FUSE_NOTIFY_INVAL_INODE:
406
printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
407
" len=%" PRIi64 "\n",
408
out.body.inval_inode.ino,
409
out.body.inval_inode.off,
410
out.body.inval_inode.len);
411
break;
412
case FUSE_NOTIFY_STORE:
413
printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
414
" size=%" PRIu32 "\n",
415
out.body.store.nodeid,
416
out.body.store.offset,
417
out.body.store.size);
418
break;
419
default:
420
break;
421
}
422
}
423
424
MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
425
bool default_permissions,
426
bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
427
uint32_t kernel_minor_version, uint32_t max_write, bool async,
428
bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
429
const char *fsname, const char *subtype, bool no_auto_init,
430
bool auto_unmount)
431
: m_daemon_id(NULL),
432
m_kernel_minor_version(kernel_minor_version),
433
m_kq(pm == KQ ? kqueue() : -1),
434
m_maxread(max_read),
435
m_maxreadahead(max_readahead),
436
m_pid(getpid()),
437
m_uniques(new std::unordered_set<uint64_t>),
438
m_pm(pm),
439
m_time_gran(time_gran),
440
m_child_pid(-1),
441
m_maxwrite(MIN(max_write, max_max_write)),
442
m_nready(-1),
443
m_quit(false),
444
m_expect_unmount(false)
445
{
446
struct sigaction sa;
447
struct iovec *iov = NULL;
448
int iovlen = 0;
449
char fdstr[15];
450
const bool trueval = true;
451
452
/*
453
* Kyua sets pwd to a testcase-unique tempdir; no need to use
454
* mkdtemp
455
*/
456
/*
457
* googletest doesn't allow ASSERT_ in constructors, so we must throw
458
* instead.
459
*/
460
if (mkdir("mountpoint" , 0755) && errno != EEXIST)
461
throw(std::system_error(errno, std::system_category(),
462
"Couldn't make mountpoint directory"));
463
464
switch (m_pm) {
465
case BLOCKING:
466
m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
467
break;
468
default:
469
m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
470
break;
471
}
472
if (m_fuse_fd < 0)
473
throw(std::system_error(errno, std::system_category(),
474
"Couldn't open /dev/fuse"));
475
476
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
477
build_iovec(&iov, &iovlen, "fspath",
478
__DECONST(void *, "mountpoint"), -1);
479
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
480
sprintf(fdstr, "%d", m_fuse_fd);
481
build_iovec(&iov, &iovlen, "fd", fdstr, -1);
482
if (m_maxread > 0) {
483
char val[12];
484
485
snprintf(val, sizeof(val), "%d", m_maxread);
486
build_iovec(&iov, &iovlen, "max_read=", &val, -1);
487
}
488
if (allow_other) {
489
build_iovec(&iov, &iovlen, "allow_other",
490
__DECONST(void*, &trueval), sizeof(bool));
491
}
492
if (default_permissions) {
493
build_iovec(&iov, &iovlen, "default_permissions",
494
__DECONST(void*, &trueval), sizeof(bool));
495
}
496
if (push_symlinks_in) {
497
build_iovec(&iov, &iovlen, "push_symlinks_in",
498
__DECONST(void*, &trueval), sizeof(bool));
499
}
500
if (ro) {
501
build_iovec(&iov, &iovlen, "ro",
502
__DECONST(void*, &trueval), sizeof(bool));
503
}
504
if (async) {
505
build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
506
sizeof(bool));
507
}
508
if (noatime) {
509
build_iovec(&iov, &iovlen, "noatime",
510
__DECONST(void*, &trueval), sizeof(bool));
511
}
512
if (noclusterr) {
513
build_iovec(&iov, &iovlen, "noclusterr",
514
__DECONST(void*, &trueval), sizeof(bool));
515
}
516
if (nointr) {
517
build_iovec(&iov, &iovlen, "nointr",
518
__DECONST(void*, &trueval), sizeof(bool));
519
} else {
520
build_iovec(&iov, &iovlen, "intr",
521
__DECONST(void*, &trueval), sizeof(bool));
522
}
523
if (auto_unmount) {
524
build_iovec(&iov, &iovlen, "auto_unmount",
525
__DECONST(void*, &trueval), sizeof(bool));
526
}
527
if (*fsname) {
528
build_iovec(&iov, &iovlen, "fsname=",
529
__DECONST(void*, fsname), -1);
530
}
531
if (*subtype) {
532
build_iovec(&iov, &iovlen, "subtype=",
533
__DECONST(void*, subtype), -1);
534
}
535
if (nmount(iov, iovlen, 0))
536
throw(std::system_error(errno, std::system_category(),
537
"Couldn't mount filesystem"));
538
free_iovec(&iov, &iovlen);
539
540
// Setup default handler
541
ON_CALL(*this, process(_, _))
542
.WillByDefault(Invoke(this, &MockFS::process_default));
543
544
if (!no_auto_init)
545
init(flags);
546
547
bzero(&sa, sizeof(sa));
548
sa.sa_handler = sigint_handler;
549
sa.sa_flags = 0; /* Don't set SA_RESTART! */
550
if (0 != sigaction(SIGUSR1, &sa, NULL))
551
throw(std::system_error(errno, std::system_category(),
552
"Couldn't handle SIGUSR1"));
553
if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
554
throw(std::system_error(errno, std::system_category(),
555
"Couldn't Couldn't start fuse thread"));
556
}
557
558
MockFS::~MockFS() {
559
kill_daemon();
560
join_daemon();
561
::unmount("mountpoint", MNT_FORCE);
562
rmdir("mountpoint");
563
if (m_kq >= 0)
564
close(m_kq);
565
}
566
567
void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
568
uint32_t inlen = in.header.len;
569
size_t fih = sizeof(in.header);
570
switch (in.header.opcode) {
571
case FUSE_LOOKUP:
572
case FUSE_RMDIR:
573
case FUSE_SYMLINK:
574
case FUSE_UNLINK:
575
EXPECT_GT(inlen, fih) << "Missing request filename";
576
// No redundant information for checking buflen
577
break;
578
case FUSE_FORGET:
579
EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
580
EXPECT_EQ((size_t)buflen, inlen);
581
break;
582
case FUSE_GETATTR:
583
EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
584
EXPECT_EQ((size_t)buflen, inlen);
585
break;
586
case FUSE_SETATTR:
587
EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
588
EXPECT_EQ((size_t)buflen, inlen);
589
break;
590
case FUSE_READLINK:
591
EXPECT_EQ(inlen, fih) << "Unexpected request body";
592
EXPECT_EQ((size_t)buflen, inlen);
593
break;
594
case FUSE_MKNOD:
595
{
596
size_t s;
597
if (m_kernel_minor_version >= 12)
598
s = sizeof(in.body.mknod);
599
else
600
s = FUSE_COMPAT_MKNOD_IN_SIZE;
601
EXPECT_GE(inlen, fih + s) << "Missing request body";
602
EXPECT_GT(inlen, fih + s) << "Missing request filename";
603
// No redundant information for checking buflen
604
break;
605
}
606
case FUSE_MKDIR:
607
EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
608
"Missing request body";
609
EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
610
"Missing request filename";
611
// No redundant information for checking buflen
612
break;
613
case FUSE_RENAME:
614
EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
615
"Missing request body";
616
EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
617
"Missing request filename";
618
// No redundant information for checking buflen
619
break;
620
case FUSE_LINK:
621
EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
622
"Missing request body";
623
EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
624
"Missing request filename";
625
// No redundant information for checking buflen
626
break;
627
case FUSE_OPEN:
628
EXPECT_EQ(inlen, fih + sizeof(in.body.open));
629
EXPECT_EQ((size_t)buflen, inlen);
630
break;
631
case FUSE_READ:
632
EXPECT_EQ(inlen, fih + sizeof(in.body.read));
633
EXPECT_EQ((size_t)buflen, inlen);
634
break;
635
case FUSE_WRITE:
636
{
637
size_t s;
638
639
if (m_kernel_minor_version >= 9)
640
s = sizeof(in.body.write);
641
else
642
s = FUSE_COMPAT_WRITE_IN_SIZE;
643
// I suppose a 0-byte write should be allowed
644
EXPECT_GE(inlen, fih + s) << "Missing request body";
645
EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
646
break;
647
}
648
case FUSE_DESTROY:
649
case FUSE_STATFS:
650
EXPECT_EQ(inlen, fih);
651
EXPECT_EQ((size_t)buflen, inlen);
652
break;
653
case FUSE_RELEASE:
654
EXPECT_EQ(inlen, fih + sizeof(in.body.release));
655
EXPECT_EQ((size_t)buflen, inlen);
656
break;
657
case FUSE_FSYNC:
658
case FUSE_FSYNCDIR:
659
EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
660
EXPECT_EQ((size_t)buflen, inlen);
661
break;
662
case FUSE_SETXATTR:
663
EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
664
"Missing request body";
665
EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
666
"Missing request attribute name";
667
// No redundant information for checking buflen
668
break;
669
case FUSE_GETXATTR:
670
EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
671
"Missing request body";
672
EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
673
"Missing request attribute name";
674
// No redundant information for checking buflen
675
break;
676
case FUSE_LISTXATTR:
677
EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
678
EXPECT_EQ((size_t)buflen, inlen);
679
break;
680
case FUSE_REMOVEXATTR:
681
EXPECT_GT(inlen, fih) << "Missing request attribute name";
682
// No redundant information for checking buflen
683
break;
684
case FUSE_FLUSH:
685
EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
686
EXPECT_EQ((size_t)buflen, inlen);
687
break;
688
case FUSE_INIT:
689
EXPECT_EQ(inlen, fih + sizeof(in.body.init));
690
EXPECT_EQ((size_t)buflen, inlen);
691
break;
692
case FUSE_IOCTL:
693
EXPECT_GE(inlen, fih + sizeof(in.body.ioctl));
694
EXPECT_EQ(inlen,
695
fih + sizeof(in.body.ioctl) + in.body.ioctl.in_size);
696
EXPECT_EQ((size_t)buflen, inlen);
697
break;
698
case FUSE_OPENDIR:
699
EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
700
EXPECT_EQ((size_t)buflen, inlen);
701
break;
702
case FUSE_READDIR:
703
EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
704
EXPECT_EQ((size_t)buflen, inlen);
705
break;
706
case FUSE_RELEASEDIR:
707
EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
708
EXPECT_EQ((size_t)buflen, inlen);
709
break;
710
case FUSE_GETLK:
711
EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
712
EXPECT_EQ((size_t)buflen, inlen);
713
break;
714
case FUSE_SETLK:
715
case FUSE_SETLKW:
716
EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
717
EXPECT_EQ((size_t)buflen, inlen);
718
break;
719
case FUSE_ACCESS:
720
EXPECT_EQ(inlen, fih + sizeof(in.body.access));
721
EXPECT_EQ((size_t)buflen, inlen);
722
break;
723
case FUSE_CREATE:
724
EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
725
"Missing request body";
726
EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
727
"Missing request filename";
728
// No redundant information for checking buflen
729
break;
730
case FUSE_INTERRUPT:
731
EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
732
EXPECT_EQ((size_t)buflen, inlen);
733
break;
734
case FUSE_FALLOCATE:
735
EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
736
EXPECT_EQ((size_t)buflen, inlen);
737
break;
738
case FUSE_BMAP:
739
EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
740
EXPECT_EQ((size_t)buflen, inlen);
741
break;
742
case FUSE_LSEEK:
743
EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
744
EXPECT_EQ((size_t)buflen, inlen);
745
break;
746
case FUSE_COPY_FILE_RANGE:
747
EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
748
EXPECT_EQ(0ul, in.body.copy_file_range.flags);
749
EXPECT_EQ((size_t)buflen, inlen);
750
break;
751
case FUSE_NOTIFY_REPLY:
752
case FUSE_BATCH_FORGET:
753
case FUSE_POLL:
754
case FUSE_READDIRPLUS:
755
FAIL() << "Unsupported opcode?";
756
default:
757
FAIL() << "Unknown opcode " << in.header.opcode;
758
}
759
/* Verify that the ticket's unique value is actually unique. */
760
if (m_uniques->find(in.header.unique) != m_uniques->end())
761
FAIL() << "Non-unique \"unique\" value";
762
m_uniques->insert(in.header.unique);
763
}
764
765
void MockFS::init(uint32_t flags) {
766
ssize_t buflen;
767
768
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
769
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
770
771
read_request(*in, buflen);
772
if (verbosity > 0)
773
debug_request(*in, buflen);
774
audit_request(*in, buflen);
775
ASSERT_EQ(FUSE_INIT, in->header.opcode);
776
777
out->header.unique = in->header.unique;
778
out->header.error = 0;
779
out->body.init.major = FUSE_KERNEL_VERSION;
780
out->body.init.minor = m_kernel_minor_version;;
781
out->body.init.flags = in->body.init.flags & flags;
782
out->body.init.max_write = m_maxwrite;
783
out->body.init.max_readahead = m_maxreadahead;
784
785
if (m_kernel_minor_version < 23) {
786
SET_OUT_HEADER_LEN(*out, init_7_22);
787
} else {
788
out->body.init.time_gran = m_time_gran;
789
SET_OUT_HEADER_LEN(*out, init);
790
}
791
792
write(m_fuse_fd, out.get(), out->header.len);
793
}
794
795
int MockFS::dup_dev_fuse()
796
{
797
return (dup(m_fuse_fd));
798
}
799
800
void MockFS::kill_daemon() {
801
m_quit = true;
802
if (m_daemon_id != NULL)
803
pthread_kill(m_daemon_id, SIGUSR1);
804
// Closing the /dev/fuse file descriptor first allows unmount to
805
// succeed even if the daemon doesn't correctly respond to commands
806
// during the unmount sequence.
807
close(m_fuse_fd);
808
m_fuse_fd = -1;
809
}
810
811
void MockFS::join_daemon() {
812
if (m_daemon_id != NULL) {
813
pthread_join(m_daemon_id, NULL);
814
m_daemon_id = NULL;
815
}
816
}
817
818
void MockFS::loop() {
819
std::vector<std::unique_ptr<mockfs_buf_out>> out;
820
821
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
822
ASSERT_TRUE(in != NULL);
823
while (!m_quit) {
824
ssize_t buflen;
825
826
bzero(in.get(), sizeof(*in));
827
read_request(*in, buflen);
828
if (m_quit)
829
break;
830
if (verbosity > 0)
831
debug_request(*in, buflen);
832
audit_request(*in, buflen);
833
if (pid_ok((pid_t)in->header.pid)) {
834
process(*in, out);
835
} else {
836
/*
837
* Reject any requests from unknown processes. Because
838
* we actually do mount a filesystem, plenty of
839
* unrelated system daemons may try to access it.
840
*/
841
if (verbosity > 1)
842
printf("\tREJECTED (wrong pid %d)\n",
843
in->header.pid);
844
process_default(*in, out);
845
}
846
for (auto &it: out)
847
write_response(*it);
848
out.clear();
849
}
850
}
851
852
int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen,
853
int expected_errno)
854
{
855
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
856
857
out->expected_errno = expected_errno;
858
out->header.unique = 0; /* 0 means asynchronous notification */
859
out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
860
out->body.inval_entry.parent = parent;
861
out->body.inval_entry.namelen = namelen;
862
strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
863
name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
864
out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
865
namelen;
866
debug_response(*out);
867
write_response(*out);
868
return 0;
869
}
870
871
int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
872
{
873
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
874
875
out->header.unique = 0; /* 0 means asynchronous notification */
876
out->header.error = FUSE_NOTIFY_INVAL_INODE;
877
out->body.inval_inode.ino = ino;
878
out->body.inval_inode.off = off;
879
out->body.inval_inode.len = len;
880
out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
881
debug_response(*out);
882
write_response(*out);
883
return 0;
884
}
885
886
int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
887
{
888
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
889
890
out->header.unique = 0; /* 0 means asynchronous notification */
891
out->header.error = FUSE_NOTIFY_STORE;
892
out->body.store.nodeid = ino;
893
out->body.store.offset = off;
894
out->body.store.size = size;
895
bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
896
out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
897
debug_response(*out);
898
write_response(*out);
899
return 0;
900
}
901
902
bool MockFS::pid_ok(pid_t pid) {
903
if (pid == m_pid) {
904
return (true);
905
} else if (pid == m_child_pid) {
906
return (true);
907
} else {
908
struct kinfo_proc *ki;
909
bool ok = false;
910
911
ki = kinfo_getproc(pid);
912
if (ki == NULL)
913
return (false);
914
/*
915
* Allow access by the aio daemon processes so that our tests
916
* can use aio functions
917
*/
918
if (0 == strncmp("aiod", ki->ki_comm, 4))
919
ok = true;
920
free(ki);
921
return (ok);
922
}
923
}
924
925
void MockFS::process_default(const mockfs_buf_in& in,
926
std::vector<std::unique_ptr<mockfs_buf_out>> &out)
927
{
928
std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
929
out0->header.unique = in.header.unique;
930
out0->header.error = -EOPNOTSUPP;
931
out0->header.len = sizeof(out0->header);
932
out.push_back(std::move(out0));
933
}
934
935
void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
936
int nready = 0;
937
fd_set readfds;
938
pollfd fds[1];
939
struct kevent changes[1];
940
struct kevent events[1];
941
struct timespec timeout_ts;
942
struct timeval timeout_tv;
943
const int timeout_ms = 999;
944
int timeout_int, nfds;
945
int fuse_fd;
946
947
switch (m_pm) {
948
case BLOCKING:
949
break;
950
case KQ:
951
timeout_ts.tv_sec = 0;
952
timeout_ts.tv_nsec = timeout_ms * 1'000'000;
953
while (nready == 0) {
954
EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
955
EV_ADD | EV_ONESHOT, 0, 0, 0);
956
nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
957
&timeout_ts);
958
if (m_quit)
959
return;
960
}
961
ASSERT_LE(0, nready) << strerror(errno);
962
ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
963
if (events[0].flags & EV_ERROR)
964
FAIL() << strerror(events[0].data);
965
else if (events[0].flags & EV_EOF)
966
FAIL() << strerror(events[0].fflags);
967
m_nready = events[0].data;
968
break;
969
case POLL:
970
timeout_int = timeout_ms;
971
fds[0].fd = m_fuse_fd;
972
fds[0].events = POLLIN;
973
while (nready == 0) {
974
nready = poll(fds, 1, timeout_int);
975
if (m_quit)
976
return;
977
}
978
ASSERT_LE(0, nready) << strerror(errno);
979
ASSERT_TRUE(fds[0].revents & POLLIN);
980
break;
981
case SELECT:
982
fuse_fd = m_fuse_fd;
983
if (fuse_fd < 0)
984
break;
985
timeout_tv.tv_sec = 0;
986
timeout_tv.tv_usec = timeout_ms * 1'000;
987
nfds = fuse_fd + 1;
988
while (nready == 0) {
989
FD_ZERO(&readfds);
990
FD_SET(fuse_fd, &readfds);
991
nready = select(nfds, &readfds, NULL, NULL,
992
&timeout_tv);
993
if (m_quit)
994
return;
995
}
996
ASSERT_LE(0, nready) << strerror(errno);
997
ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
998
break;
999
default:
1000
FAIL() << "not yet implemented";
1001
}
1002
res = read(m_fuse_fd, &in, sizeof(in));
1003
1004
if (res < 0 && errno == ENODEV && m_expect_unmount) {
1005
/* The kernel unmounted us, as expected. */
1006
m_quit = true;
1007
}
1008
if (res < 0 && errno != EBADF && !m_quit) {
1009
m_quit = true;
1010
FAIL() << "read: " << strerror(errno);
1011
}
1012
ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
1013
/*
1014
* Inconsistently, fuse_in_header.len is the size of the entire
1015
* request,including header, even though fuse_out_header.len excludes
1016
* the size of the header.
1017
*/
1018
ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
1019
}
1020
1021
void MockFS::write_response(const mockfs_buf_out &out) {
1022
fd_set writefds;
1023
pollfd fds[1];
1024
struct kevent changes[1];
1025
struct kevent events[1];
1026
int nready, nfds;
1027
ssize_t r;
1028
1029
switch (m_pm) {
1030
case BLOCKING:
1031
break;
1032
case KQ:
1033
EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1034
EV_ADD | EV_ONESHOT, 0, 0, 0);
1035
nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1036
NULL);
1037
ASSERT_LE(0, nready) << strerror(errno);
1038
ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1039
if (events[0].flags & EV_ERROR)
1040
FAIL() << strerror(events[0].data);
1041
else if (events[0].flags & EV_EOF)
1042
FAIL() << strerror(events[0].fflags);
1043
m_nready = events[0].data;
1044
break;
1045
case POLL:
1046
fds[0].fd = m_fuse_fd;
1047
fds[0].events = POLLOUT;
1048
nready = poll(fds, 1, INFTIM);
1049
ASSERT_LE(0, nready) << strerror(errno);
1050
ASSERT_EQ(1, nready) << "NULL timeout expired?";
1051
ASSERT_TRUE(fds[0].revents & POLLOUT);
1052
break;
1053
case SELECT:
1054
FD_ZERO(&writefds);
1055
FD_SET(m_fuse_fd, &writefds);
1056
nfds = m_fuse_fd + 1;
1057
nready = select(nfds, NULL, &writefds, NULL, NULL);
1058
ASSERT_LE(0, nready) << strerror(errno);
1059
ASSERT_EQ(1, nready) << "NULL timeout expired?";
1060
ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1061
break;
1062
default:
1063
FAIL() << "not yet implemented";
1064
}
1065
r = write(m_fuse_fd, &out, out.header.len);
1066
if (out.expected_errno) {
1067
ASSERT_EQ(-1, r);
1068
ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1069
} else {
1070
if (r <= 0 && errno == EINVAL) {
1071
printf("Failed to write response. unique=%" PRIu64
1072
":\n", out.header.unique);
1073
}
1074
ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1075
}
1076
}
1077
1078
void* MockFS::service(void *pthr_data) {
1079
MockFS *mock_fs = (MockFS*)pthr_data;
1080
1081
mock_fs->loop();
1082
1083
return (NULL);
1084
}
1085
1086
void MockFS::unmount() {
1087
::unmount("mountpoint", 0);
1088
}
1089
1090