Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/mockfs.cc
39537 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_LINK:
245
printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
246
break;
247
case FUSE_LISTXATTR:
248
printf(" size=%" PRIu32, in.body.listxattr.size);
249
break;
250
case FUSE_LOOKUP:
251
printf(" %s", in.body.lookup);
252
break;
253
case FUSE_LSEEK:
254
switch (in.body.lseek.whence) {
255
case SEEK_HOLE:
256
printf(" SEEK_HOLE offset=%jd",
257
in.body.lseek.offset);
258
break;
259
case SEEK_DATA:
260
printf(" SEEK_DATA offset=%jd",
261
in.body.lseek.offset);
262
break;
263
default:
264
printf(" whence=%u offset=%jd",
265
in.body.lseek.whence, in.body.lseek.offset);
266
break;
267
}
268
break;
269
case FUSE_MKDIR:
270
name = (const char*)in.body.bytes +
271
sizeof(fuse_mkdir_in);
272
printf(" name=%s mode=%#o umask=%#o", name,
273
in.body.mkdir.mode, in.body.mkdir.umask);
274
break;
275
case FUSE_MKNOD:
276
if (m_kernel_minor_version >= 12)
277
name = (const char*)in.body.bytes +
278
sizeof(fuse_mknod_in);
279
else
280
name = (const char*)in.body.bytes +
281
FUSE_COMPAT_MKNOD_IN_SIZE;
282
printf(" mode=%#o rdev=%x umask=%#o name=%s",
283
in.body.mknod.mode, in.body.mknod.rdev,
284
in.body.mknod.umask, name);
285
break;
286
case FUSE_OPEN:
287
printf(" flags=%#x", in.body.open.flags);
288
break;
289
case FUSE_OPENDIR:
290
printf(" flags=%#x", in.body.opendir.flags);
291
break;
292
case FUSE_READ:
293
printf(" offset=%" PRIu64 " size=%u",
294
in.body.read.offset,
295
in.body.read.size);
296
if (verbosity > 1)
297
printf(" flags=%#x", in.body.read.flags);
298
break;
299
case FUSE_READDIR:
300
printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
301
in.body.readdir.fh, in.body.readdir.offset,
302
in.body.readdir.size);
303
break;
304
case FUSE_RELEASE:
305
printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
306
in.body.release.fh,
307
in.body.release.flags,
308
in.body.release.lock_owner);
309
break;
310
case FUSE_RENAME:
311
{
312
const char *src = (const char*)in.body.bytes +
313
sizeof(fuse_rename_in);
314
const char *dst = src + strlen(src) + 1;
315
printf(" src=%s newdir=%" PRIu64 " dst=%s",
316
src, in.body.rename.newdir, dst);
317
}
318
break;
319
case FUSE_SETATTR:
320
if (verbosity <= 1) {
321
printf(" valid=%#x", in.body.setattr.valid);
322
break;
323
}
324
if (in.body.setattr.valid & FATTR_MODE)
325
printf(" mode=%#o", in.body.setattr.mode);
326
if (in.body.setattr.valid & FATTR_UID)
327
printf(" uid=%u", in.body.setattr.uid);
328
if (in.body.setattr.valid & FATTR_GID)
329
printf(" gid=%u", in.body.setattr.gid);
330
if (in.body.setattr.valid & FATTR_SIZE)
331
printf(" size=%" PRIu64, in.body.setattr.size);
332
if (in.body.setattr.valid & FATTR_ATIME)
333
printf(" atime=%" PRIu64 ".%u",
334
in.body.setattr.atime,
335
in.body.setattr.atimensec);
336
if (in.body.setattr.valid & FATTR_MTIME)
337
printf(" mtime=%" PRIu64 ".%u",
338
in.body.setattr.mtime,
339
in.body.setattr.mtimensec);
340
if (in.body.setattr.valid & FATTR_FH)
341
printf(" fh=%" PRIu64 "", in.body.setattr.fh);
342
break;
343
case FUSE_SETLK:
344
printf(" fh=%#" PRIx64 " owner=%" PRIu64
345
" type=%u pid=%u",
346
in.body.setlk.fh, in.body.setlk.owner,
347
in.body.setlk.lk.type,
348
in.body.setlk.lk.pid);
349
if (verbosity >= 2) {
350
printf(" range=[%" PRIi64 ":%" PRIi64 "]",
351
in.body.setlk.lk.start,
352
in.body.setlk.lk.end);
353
}
354
break;
355
case FUSE_SETXATTR:
356
/*
357
* In theory neither the xattr name and value need be
358
* ASCII, but in this test suite they always are.
359
*/
360
name = (const char*)in.body.bytes +
361
sizeof(fuse_setxattr_in);
362
value = name + strlen(name) + 1;
363
printf(" %s=%s", name, value);
364
break;
365
case FUSE_WRITE:
366
printf(" fh=%#" PRIx64 " offset=%" PRIu64
367
" size=%u write_flags=%u",
368
in.body.write.fh,
369
in.body.write.offset, in.body.write.size,
370
in.body.write.write_flags);
371
if (verbosity > 1)
372
printf(" flags=%#x", in.body.write.flags);
373
break;
374
default:
375
break;
376
}
377
printf("\n");
378
}
379
380
/*
381
* Debug a FUSE response.
382
*
383
* This is mostly useful for asynchronous notifications, which don't correspond
384
* to any request
385
*/
386
void MockFS::debug_response(const mockfs_buf_out &out) {
387
const char *name;
388
389
if (verbosity == 0)
390
return;
391
392
switch (out.header.error) {
393
case FUSE_NOTIFY_INVAL_ENTRY:
394
name = (const char*)out.body.bytes +
395
sizeof(fuse_notify_inval_entry_out);
396
printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
397
out.body.inval_entry.parent, name);
398
break;
399
case FUSE_NOTIFY_INVAL_INODE:
400
printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
401
" len=%" PRIi64 "\n",
402
out.body.inval_inode.ino,
403
out.body.inval_inode.off,
404
out.body.inval_inode.len);
405
break;
406
case FUSE_NOTIFY_STORE:
407
printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
408
" size=%" PRIu32 "\n",
409
out.body.store.nodeid,
410
out.body.store.offset,
411
out.body.store.size);
412
break;
413
default:
414
break;
415
}
416
}
417
418
MockFS::MockFS(int max_read, int max_readahead, bool allow_other,
419
bool default_permissions,
420
bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
421
uint32_t kernel_minor_version, uint32_t max_write, bool async,
422
bool noclusterr, unsigned time_gran, bool nointr, bool noatime,
423
const char *fsname, const char *subtype, bool no_auto_init)
424
: m_daemon_id(NULL),
425
m_kernel_minor_version(kernel_minor_version),
426
m_kq(pm == KQ ? kqueue() : -1),
427
m_maxread(max_read),
428
m_maxreadahead(max_readahead),
429
m_pid(getpid()),
430
m_uniques(new std::unordered_set<uint64_t>),
431
m_pm(pm),
432
m_time_gran(time_gran),
433
m_child_pid(-1),
434
m_maxwrite(MIN(max_write, max_max_write)),
435
m_nready(-1),
436
m_quit(false)
437
{
438
struct sigaction sa;
439
struct iovec *iov = NULL;
440
int iovlen = 0;
441
char fdstr[15];
442
const bool trueval = true;
443
444
/*
445
* Kyua sets pwd to a testcase-unique tempdir; no need to use
446
* mkdtemp
447
*/
448
/*
449
* googletest doesn't allow ASSERT_ in constructors, so we must throw
450
* instead.
451
*/
452
if (mkdir("mountpoint" , 0755) && errno != EEXIST)
453
throw(std::system_error(errno, std::system_category(),
454
"Couldn't make mountpoint directory"));
455
456
switch (m_pm) {
457
case BLOCKING:
458
m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
459
break;
460
default:
461
m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
462
break;
463
}
464
if (m_fuse_fd < 0)
465
throw(std::system_error(errno, std::system_category(),
466
"Couldn't open /dev/fuse"));
467
468
build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
469
build_iovec(&iov, &iovlen, "fspath",
470
__DECONST(void *, "mountpoint"), -1);
471
build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
472
sprintf(fdstr, "%d", m_fuse_fd);
473
build_iovec(&iov, &iovlen, "fd", fdstr, -1);
474
if (m_maxread > 0) {
475
char val[12];
476
477
snprintf(val, sizeof(val), "%d", m_maxread);
478
build_iovec(&iov, &iovlen, "max_read=", &val, -1);
479
}
480
if (allow_other) {
481
build_iovec(&iov, &iovlen, "allow_other",
482
__DECONST(void*, &trueval), sizeof(bool));
483
}
484
if (default_permissions) {
485
build_iovec(&iov, &iovlen, "default_permissions",
486
__DECONST(void*, &trueval), sizeof(bool));
487
}
488
if (push_symlinks_in) {
489
build_iovec(&iov, &iovlen, "push_symlinks_in",
490
__DECONST(void*, &trueval), sizeof(bool));
491
}
492
if (ro) {
493
build_iovec(&iov, &iovlen, "ro",
494
__DECONST(void*, &trueval), sizeof(bool));
495
}
496
if (async) {
497
build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
498
sizeof(bool));
499
}
500
if (noatime) {
501
build_iovec(&iov, &iovlen, "noatime",
502
__DECONST(void*, &trueval), sizeof(bool));
503
}
504
if (noclusterr) {
505
build_iovec(&iov, &iovlen, "noclusterr",
506
__DECONST(void*, &trueval), sizeof(bool));
507
}
508
if (nointr) {
509
build_iovec(&iov, &iovlen, "nointr",
510
__DECONST(void*, &trueval), sizeof(bool));
511
} else {
512
build_iovec(&iov, &iovlen, "intr",
513
__DECONST(void*, &trueval), sizeof(bool));
514
}
515
if (*fsname) {
516
build_iovec(&iov, &iovlen, "fsname=",
517
__DECONST(void*, fsname), -1);
518
}
519
if (*subtype) {
520
build_iovec(&iov, &iovlen, "subtype=",
521
__DECONST(void*, subtype), -1);
522
}
523
if (nmount(iov, iovlen, 0))
524
throw(std::system_error(errno, std::system_category(),
525
"Couldn't mount filesystem"));
526
free_iovec(&iov, &iovlen);
527
528
// Setup default handler
529
ON_CALL(*this, process(_, _))
530
.WillByDefault(Invoke(this, &MockFS::process_default));
531
532
if (!no_auto_init)
533
init(flags);
534
535
bzero(&sa, sizeof(sa));
536
sa.sa_handler = sigint_handler;
537
sa.sa_flags = 0; /* Don't set SA_RESTART! */
538
if (0 != sigaction(SIGUSR1, &sa, NULL))
539
throw(std::system_error(errno, std::system_category(),
540
"Couldn't handle SIGUSR1"));
541
if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
542
throw(std::system_error(errno, std::system_category(),
543
"Couldn't Couldn't start fuse thread"));
544
}
545
546
MockFS::~MockFS() {
547
kill_daemon();
548
join_daemon();
549
::unmount("mountpoint", MNT_FORCE);
550
rmdir("mountpoint");
551
if (m_kq >= 0)
552
close(m_kq);
553
}
554
555
void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
556
uint32_t inlen = in.header.len;
557
size_t fih = sizeof(in.header);
558
switch (in.header.opcode) {
559
case FUSE_LOOKUP:
560
case FUSE_RMDIR:
561
case FUSE_SYMLINK:
562
case FUSE_UNLINK:
563
EXPECT_GT(inlen, fih) << "Missing request filename";
564
// No redundant information for checking buflen
565
break;
566
case FUSE_FORGET:
567
EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
568
EXPECT_EQ((size_t)buflen, inlen);
569
break;
570
case FUSE_GETATTR:
571
EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
572
EXPECT_EQ((size_t)buflen, inlen);
573
break;
574
case FUSE_SETATTR:
575
EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
576
EXPECT_EQ((size_t)buflen, inlen);
577
break;
578
case FUSE_READLINK:
579
EXPECT_EQ(inlen, fih) << "Unexpected request body";
580
EXPECT_EQ((size_t)buflen, inlen);
581
break;
582
case FUSE_MKNOD:
583
{
584
size_t s;
585
if (m_kernel_minor_version >= 12)
586
s = sizeof(in.body.mknod);
587
else
588
s = FUSE_COMPAT_MKNOD_IN_SIZE;
589
EXPECT_GE(inlen, fih + s) << "Missing request body";
590
EXPECT_GT(inlen, fih + s) << "Missing request filename";
591
// No redundant information for checking buflen
592
break;
593
}
594
case FUSE_MKDIR:
595
EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
596
"Missing request body";
597
EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
598
"Missing request filename";
599
// No redundant information for checking buflen
600
break;
601
case FUSE_RENAME:
602
EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
603
"Missing request body";
604
EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
605
"Missing request filename";
606
// No redundant information for checking buflen
607
break;
608
case FUSE_LINK:
609
EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
610
"Missing request body";
611
EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
612
"Missing request filename";
613
// No redundant information for checking buflen
614
break;
615
case FUSE_OPEN:
616
EXPECT_EQ(inlen, fih + sizeof(in.body.open));
617
EXPECT_EQ((size_t)buflen, inlen);
618
break;
619
case FUSE_READ:
620
EXPECT_EQ(inlen, fih + sizeof(in.body.read));
621
EXPECT_EQ((size_t)buflen, inlen);
622
break;
623
case FUSE_WRITE:
624
{
625
size_t s;
626
627
if (m_kernel_minor_version >= 9)
628
s = sizeof(in.body.write);
629
else
630
s = FUSE_COMPAT_WRITE_IN_SIZE;
631
// I suppose a 0-byte write should be allowed
632
EXPECT_GE(inlen, fih + s) << "Missing request body";
633
EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
634
break;
635
}
636
case FUSE_DESTROY:
637
case FUSE_STATFS:
638
EXPECT_EQ(inlen, fih);
639
EXPECT_EQ((size_t)buflen, inlen);
640
break;
641
case FUSE_RELEASE:
642
EXPECT_EQ(inlen, fih + sizeof(in.body.release));
643
EXPECT_EQ((size_t)buflen, inlen);
644
break;
645
case FUSE_FSYNC:
646
case FUSE_FSYNCDIR:
647
EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
648
EXPECT_EQ((size_t)buflen, inlen);
649
break;
650
case FUSE_SETXATTR:
651
EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
652
"Missing request body";
653
EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
654
"Missing request attribute name";
655
// No redundant information for checking buflen
656
break;
657
case FUSE_GETXATTR:
658
EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
659
"Missing request body";
660
EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
661
"Missing request attribute name";
662
// No redundant information for checking buflen
663
break;
664
case FUSE_LISTXATTR:
665
EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
666
EXPECT_EQ((size_t)buflen, inlen);
667
break;
668
case FUSE_REMOVEXATTR:
669
EXPECT_GT(inlen, fih) << "Missing request attribute name";
670
// No redundant information for checking buflen
671
break;
672
case FUSE_FLUSH:
673
EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
674
EXPECT_EQ((size_t)buflen, inlen);
675
break;
676
case FUSE_INIT:
677
EXPECT_EQ(inlen, fih + sizeof(in.body.init));
678
EXPECT_EQ((size_t)buflen, inlen);
679
break;
680
case FUSE_OPENDIR:
681
EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
682
EXPECT_EQ((size_t)buflen, inlen);
683
break;
684
case FUSE_READDIR:
685
EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
686
EXPECT_EQ((size_t)buflen, inlen);
687
break;
688
case FUSE_RELEASEDIR:
689
EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
690
EXPECT_EQ((size_t)buflen, inlen);
691
break;
692
case FUSE_GETLK:
693
EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
694
EXPECT_EQ((size_t)buflen, inlen);
695
break;
696
case FUSE_SETLK:
697
case FUSE_SETLKW:
698
EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
699
EXPECT_EQ((size_t)buflen, inlen);
700
break;
701
case FUSE_ACCESS:
702
EXPECT_EQ(inlen, fih + sizeof(in.body.access));
703
EXPECT_EQ((size_t)buflen, inlen);
704
break;
705
case FUSE_CREATE:
706
EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
707
"Missing request body";
708
EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
709
"Missing request filename";
710
// No redundant information for checking buflen
711
break;
712
case FUSE_INTERRUPT:
713
EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
714
EXPECT_EQ((size_t)buflen, inlen);
715
break;
716
case FUSE_FALLOCATE:
717
EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate));
718
EXPECT_EQ((size_t)buflen, inlen);
719
break;
720
case FUSE_BMAP:
721
EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
722
EXPECT_EQ((size_t)buflen, inlen);
723
break;
724
case FUSE_LSEEK:
725
EXPECT_EQ(inlen, fih + sizeof(in.body.lseek));
726
EXPECT_EQ((size_t)buflen, inlen);
727
break;
728
case FUSE_COPY_FILE_RANGE:
729
EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range));
730
EXPECT_EQ(0ul, in.body.copy_file_range.flags);
731
EXPECT_EQ((size_t)buflen, inlen);
732
break;
733
case FUSE_NOTIFY_REPLY:
734
case FUSE_BATCH_FORGET:
735
case FUSE_IOCTL:
736
case FUSE_POLL:
737
case FUSE_READDIRPLUS:
738
FAIL() << "Unsupported opcode?";
739
default:
740
FAIL() << "Unknown opcode " << in.header.opcode;
741
}
742
/* Verify that the ticket's unique value is actually unique. */
743
if (m_uniques->find(in.header.unique) != m_uniques->end())
744
FAIL() << "Non-unique \"unique\" value";
745
m_uniques->insert(in.header.unique);
746
}
747
748
void MockFS::init(uint32_t flags) {
749
ssize_t buflen;
750
751
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
752
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
753
754
read_request(*in, buflen);
755
if (verbosity > 0)
756
debug_request(*in, buflen);
757
audit_request(*in, buflen);
758
ASSERT_EQ(FUSE_INIT, in->header.opcode);
759
760
out->header.unique = in->header.unique;
761
out->header.error = 0;
762
out->body.init.major = FUSE_KERNEL_VERSION;
763
out->body.init.minor = m_kernel_minor_version;;
764
out->body.init.flags = in->body.init.flags & flags;
765
out->body.init.max_write = m_maxwrite;
766
out->body.init.max_readahead = m_maxreadahead;
767
768
if (m_kernel_minor_version < 23) {
769
SET_OUT_HEADER_LEN(*out, init_7_22);
770
} else {
771
out->body.init.time_gran = m_time_gran;
772
SET_OUT_HEADER_LEN(*out, init);
773
}
774
775
write(m_fuse_fd, out.get(), out->header.len);
776
}
777
778
void MockFS::kill_daemon() {
779
m_quit = true;
780
if (m_daemon_id != NULL)
781
pthread_kill(m_daemon_id, SIGUSR1);
782
// Closing the /dev/fuse file descriptor first allows unmount to
783
// succeed even if the daemon doesn't correctly respond to commands
784
// during the unmount sequence.
785
close(m_fuse_fd);
786
m_fuse_fd = -1;
787
}
788
789
void MockFS::join_daemon() {
790
if (m_daemon_id != NULL) {
791
pthread_join(m_daemon_id, NULL);
792
m_daemon_id = NULL;
793
}
794
}
795
796
void MockFS::loop() {
797
std::vector<std::unique_ptr<mockfs_buf_out>> out;
798
799
std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
800
ASSERT_TRUE(in != NULL);
801
while (!m_quit) {
802
ssize_t buflen;
803
804
bzero(in.get(), sizeof(*in));
805
read_request(*in, buflen);
806
if (m_quit)
807
break;
808
if (verbosity > 0)
809
debug_request(*in, buflen);
810
audit_request(*in, buflen);
811
if (pid_ok((pid_t)in->header.pid)) {
812
process(*in, out);
813
} else {
814
/*
815
* Reject any requests from unknown processes. Because
816
* we actually do mount a filesystem, plenty of
817
* unrelated system daemons may try to access it.
818
*/
819
if (verbosity > 1)
820
printf("\tREJECTED (wrong pid %d)\n",
821
in->header.pid);
822
process_default(*in, out);
823
}
824
for (auto &it: out)
825
write_response(*it);
826
out.clear();
827
}
828
}
829
830
int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
831
{
832
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
833
834
out->header.unique = 0; /* 0 means asynchronous notification */
835
out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
836
out->body.inval_entry.parent = parent;
837
out->body.inval_entry.namelen = namelen;
838
strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
839
name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
840
out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
841
namelen;
842
debug_response(*out);
843
write_response(*out);
844
return 0;
845
}
846
847
int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
848
{
849
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
850
851
out->header.unique = 0; /* 0 means asynchronous notification */
852
out->header.error = FUSE_NOTIFY_INVAL_INODE;
853
out->body.inval_inode.ino = ino;
854
out->body.inval_inode.off = off;
855
out->body.inval_inode.len = len;
856
out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
857
debug_response(*out);
858
write_response(*out);
859
return 0;
860
}
861
862
int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
863
{
864
std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
865
866
out->header.unique = 0; /* 0 means asynchronous notification */
867
out->header.error = FUSE_NOTIFY_STORE;
868
out->body.store.nodeid = ino;
869
out->body.store.offset = off;
870
out->body.store.size = size;
871
bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
872
out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
873
debug_response(*out);
874
write_response(*out);
875
return 0;
876
}
877
878
bool MockFS::pid_ok(pid_t pid) {
879
if (pid == m_pid) {
880
return (true);
881
} else if (pid == m_child_pid) {
882
return (true);
883
} else {
884
struct kinfo_proc *ki;
885
bool ok = false;
886
887
ki = kinfo_getproc(pid);
888
if (ki == NULL)
889
return (false);
890
/*
891
* Allow access by the aio daemon processes so that our tests
892
* can use aio functions
893
*/
894
if (0 == strncmp("aiod", ki->ki_comm, 4))
895
ok = true;
896
free(ki);
897
return (ok);
898
}
899
}
900
901
void MockFS::process_default(const mockfs_buf_in& in,
902
std::vector<std::unique_ptr<mockfs_buf_out>> &out)
903
{
904
std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
905
out0->header.unique = in.header.unique;
906
out0->header.error = -EOPNOTSUPP;
907
out0->header.len = sizeof(out0->header);
908
out.push_back(std::move(out0));
909
}
910
911
void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
912
int nready = 0;
913
fd_set readfds;
914
pollfd fds[1];
915
struct kevent changes[1];
916
struct kevent events[1];
917
struct timespec timeout_ts;
918
struct timeval timeout_tv;
919
const int timeout_ms = 999;
920
int timeout_int, nfds;
921
int fuse_fd;
922
923
switch (m_pm) {
924
case BLOCKING:
925
break;
926
case KQ:
927
timeout_ts.tv_sec = 0;
928
timeout_ts.tv_nsec = timeout_ms * 1'000'000;
929
while (nready == 0) {
930
EV_SET(&changes[0], m_fuse_fd, EVFILT_READ,
931
EV_ADD | EV_ONESHOT, 0, 0, 0);
932
nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
933
&timeout_ts);
934
if (m_quit)
935
return;
936
}
937
ASSERT_LE(0, nready) << strerror(errno);
938
ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
939
if (events[0].flags & EV_ERROR)
940
FAIL() << strerror(events[0].data);
941
else if (events[0].flags & EV_EOF)
942
FAIL() << strerror(events[0].fflags);
943
m_nready = events[0].data;
944
break;
945
case POLL:
946
timeout_int = timeout_ms;
947
fds[0].fd = m_fuse_fd;
948
fds[0].events = POLLIN;
949
while (nready == 0) {
950
nready = poll(fds, 1, timeout_int);
951
if (m_quit)
952
return;
953
}
954
ASSERT_LE(0, nready) << strerror(errno);
955
ASSERT_TRUE(fds[0].revents & POLLIN);
956
break;
957
case SELECT:
958
fuse_fd = m_fuse_fd;
959
if (fuse_fd < 0)
960
break;
961
timeout_tv.tv_sec = 0;
962
timeout_tv.tv_usec = timeout_ms * 1'000;
963
nfds = fuse_fd + 1;
964
while (nready == 0) {
965
FD_ZERO(&readfds);
966
FD_SET(fuse_fd, &readfds);
967
nready = select(nfds, &readfds, NULL, NULL,
968
&timeout_tv);
969
if (m_quit)
970
return;
971
}
972
ASSERT_LE(0, nready) << strerror(errno);
973
ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds));
974
break;
975
default:
976
FAIL() << "not yet implemented";
977
}
978
res = read(m_fuse_fd, &in, sizeof(in));
979
980
if (res < 0 && !m_quit) {
981
m_quit = true;
982
FAIL() << "read: " << strerror(errno);
983
}
984
ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
985
/*
986
* Inconsistently, fuse_in_header.len is the size of the entire
987
* request,including header, even though fuse_out_header.len excludes
988
* the size of the header.
989
*/
990
ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
991
}
992
993
void MockFS::write_response(const mockfs_buf_out &out) {
994
fd_set writefds;
995
pollfd fds[1];
996
struct kevent changes[1];
997
struct kevent events[1];
998
int nready, nfds;
999
ssize_t r;
1000
1001
switch (m_pm) {
1002
case BLOCKING:
1003
break;
1004
case KQ:
1005
EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE,
1006
EV_ADD | EV_ONESHOT, 0, 0, 0);
1007
nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
1008
NULL);
1009
ASSERT_LE(0, nready) << strerror(errno);
1010
ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
1011
if (events[0].flags & EV_ERROR)
1012
FAIL() << strerror(events[0].data);
1013
else if (events[0].flags & EV_EOF)
1014
FAIL() << strerror(events[0].fflags);
1015
m_nready = events[0].data;
1016
break;
1017
case POLL:
1018
fds[0].fd = m_fuse_fd;
1019
fds[0].events = POLLOUT;
1020
nready = poll(fds, 1, INFTIM);
1021
ASSERT_LE(0, nready) << strerror(errno);
1022
ASSERT_EQ(1, nready) << "NULL timeout expired?";
1023
ASSERT_TRUE(fds[0].revents & POLLOUT);
1024
break;
1025
case SELECT:
1026
FD_ZERO(&writefds);
1027
FD_SET(m_fuse_fd, &writefds);
1028
nfds = m_fuse_fd + 1;
1029
nready = select(nfds, NULL, &writefds, NULL, NULL);
1030
ASSERT_LE(0, nready) << strerror(errno);
1031
ASSERT_EQ(1, nready) << "NULL timeout expired?";
1032
ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
1033
break;
1034
default:
1035
FAIL() << "not yet implemented";
1036
}
1037
r = write(m_fuse_fd, &out, out.header.len);
1038
if (out.expected_errno) {
1039
ASSERT_EQ(-1, r);
1040
ASSERT_EQ(out.expected_errno, errno) << strerror(errno);
1041
} else {
1042
if (r <= 0 && errno == EINVAL) {
1043
printf("Failed to write response. unique=%" PRIu64
1044
":\n", out.header.unique);
1045
}
1046
ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
1047
}
1048
}
1049
1050
void* MockFS::service(void *pthr_data) {
1051
MockFS *mock_fs = (MockFS*)pthr_data;
1052
1053
mock_fs->loop();
1054
1055
return (NULL);
1056
}
1057
1058
void MockFS::unmount() {
1059
::unmount("mountpoint", 0);
1060
}
1061
1062