View | Details | Raw Unified | Return to bug 245689
Collapse All | Expand All

(-)sys/fs/fuse/fuse_internal.c (-1 / +7 lines)
Lines 158-163 Link Here
158
	return 0;
158
	return 0;
159
}
159
}
160
160
161
SDT_PROBE_DEFINE0(fusefs, , internal, access_vadmin);
161
/* Synchronously send a FUSE_ACCESS operation */
162
/* Synchronously send a FUSE_ACCESS operation */
162
int
163
int
163
fuse_internal_access(struct vnode *vp,
164
fuse_internal_access(struct vnode *vp,
Lines 213-219 Link Here
213
	if (!fsess_isimpl(mp, FUSE_ACCESS))
214
	if (!fsess_isimpl(mp, FUSE_ACCESS))
214
		return 0;
215
		return 0;
215
216
216
	if ((mode & (VWRITE | VAPPEND | VADMIN)) != 0)
217
	if (mode & VADMIN) {
218
		// The FUSE protocol doesn't have an equivalent of VADMIN, so
219
		// it's a bug if we ever reach this point with that bit set.
220
		SDT_PROBE0(fusefs, , internal, access_vadmin);
221
	}
222
	if ((mode & (VWRITE | VAPPEND)) != 0)
217
		mask |= W_OK;
223
		mask |= W_OK;
218
	if ((mode & VREAD) != 0)
224
	if ((mode & VREAD) != 0)
219
		mask |= R_OK;
225
		mask |= R_OK;
(-)sys/fs/fuse/fuse_vnops.c (-5 / +18 lines)
Lines 235-240 Link Here
235
{
235
{
236
	struct mount *mp = vnode_mount(vp);
236
	struct mount *mp = vnode_mount(vp);
237
	struct fuse_data *data = fuse_get_mpdata(mp);
237
	struct fuse_data *data = fuse_get_mpdata(mp);
238
	int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
238
239
239
	/*
240
	/*
240
	 * Kernel-invoked always succeeds.
241
	 * Kernel-invoked always succeeds.
Lines 248-259 Link Here
248
	 */
249
	 */
249
	switch (ns) {
250
	switch (ns) {
250
	case EXTATTR_NAMESPACE_SYSTEM:
251
	case EXTATTR_NAMESPACE_SYSTEM:
251
		if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
252
		if (default_permissions) {
252
			return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
253
			return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
253
		}
254
		}
254
		/* FALLTHROUGH */
255
		/* FALLTHROUGH */
255
	case EXTATTR_NAMESPACE_USER:
256
	case EXTATTR_NAMESPACE_USER:
256
		return (fuse_internal_access(vp, accmode, td, cred));
257
		if (default_permissions) {
258
			return (fuse_internal_access(vp, accmode, td, cred));
259
		} else {
260
			return (0);
261
		}
257
	default:
262
	default:
258
		return (EPERM);
263
		return (EPERM);
259
	}
264
	}
Lines 985-990 Link Here
985
	int wantparent = flags & (LOCKPARENT | WANTPARENT);
990
	int wantparent = flags & (LOCKPARENT | WANTPARENT);
986
	int islastcn = flags & ISLASTCN;
991
	int islastcn = flags & ISLASTCN;
987
	struct mount *mp = vnode_mount(dvp);
992
	struct mount *mp = vnode_mount(dvp);
993
	struct fuse_data *data = fuse_get_mpdata(mp);
994
	int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
988
995
989
	int err = 0;
996
	int err = 0;
990
	int lookup_err = 0;
997
	int lookup_err = 0;
Lines 1108-1114 Link Here
1108
	if (lookup_err) {
1115
	if (lookup_err) {
1109
		/* Entry not found */
1116
		/* Entry not found */
1110
		if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1117
		if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1111
			err = fuse_internal_access(dvp, VWRITE, td, cred);
1118
			if (default_permissions)
1119
				err = fuse_internal_access(dvp, VWRITE, td,
1120
				    cred);
1121
			else
1122
				err = 0;
1112
			if (!err) {
1123
			if (!err) {
1113
				/*
1124
				/*
1114
				 * Set the SAVENAME flag to hold onto the
1125
				 * Set the SAVENAME flag to hold onto the
Lines 1191-1197 Link Here
1191
				&fvdat->entry_cache_timeout);
1202
				&fvdat->entry_cache_timeout);
1192
1203
1193
			if ((nameiop == DELETE || nameiop == RENAME) &&
1204
			if ((nameiop == DELETE || nameiop == RENAME) &&
1194
				islastcn)
1205
				islastcn && default_permissions)
1195
			{
1206
			{
1196
				struct vattr dvattr;
1207
				struct vattr dvattr;
1197
1208
Lines 1828-1834 Link Here
1828
	if (vfs_isrdonly(mp))
1839
	if (vfs_isrdonly(mp))
1829
		return EROFS;
1840
		return EROFS;
1830
1841
1831
	err = fuse_internal_access(vp, accmode, td, cred);
1842
	if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
1843
		err = fuse_internal_access(vp, accmode, td, cred);
1844
	}
1832
	if (err)
1845
	if (err)
1833
		return err;
1846
		return err;
1834
	else
1847
	else
(-)tests/sys/fs/fusefs/access.cc (+183 lines)
Lines 31-36 Link Here
31
 */
31
 */
32
32
33
extern "C" {
33
extern "C" {
34
#include <sys/types.h>
35
#include <sys/extattr.h>
36
34
#include <fcntl.h>
37
#include <fcntl.h>
35
#include <unistd.h>
38
#include <unistd.h>
36
}
39
}
Lines 42-51 Link Here
42
45
43
class Access: public FuseTest {
46
class Access: public FuseTest {
44
public:
47
public:
48
virtual void SetUp() {
49
	FuseTest::SetUp();
50
	// Clear the default FUSE_ACCESS expectation
51
	Mock::VerifyAndClearExpectations(m_mock);
52
}
53
45
void expect_lookup(const char *relpath, uint64_t ino)
54
void expect_lookup(const char *relpath, uint64_t ino)
46
{
55
{
47
	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
56
	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
48
}
57
}
58
59
/* 
60
 * Expect tha FUSE_ACCESS will never be called for the given inode, with any
61
 * bits in the supplied access_mask set
62
 */
63
void expect_noaccess(uint64_t ino, mode_t access_mask)
64
{
65
	EXPECT_CALL(*m_mock, process(
66
		ResultOf([=](auto in) {
67
			return (in.header.opcode == FUSE_ACCESS &&
68
				in.header.nodeid == ino &&
69
				in.body.access.mask & access_mask);
70
		}, Eq(true)),
71
		_)
72
	).Times(0);
73
}
74
49
};
75
};
50
76
51
class RofsAccess: public Access {
77
class RofsAccess: public Access {
Lines 56-61 Link Here
56
}
82
}
57
};
83
};
58
84
85
/*
86
 * Change the mode of a file.
87
 *
88
 * There should never be a FUSE_ACCESS sent for this operation, except possibly
89
 * during the lookup phase.
90
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
91
 */
92
TEST_F(Access, chmod)
93
{
94
	const char FULLPATH[] = "mountpoint/some_file.txt";
95
	const char RELPATH[] = "some_file.txt";
96
	const uint64_t ino = 42;
97
	const mode_t newmode = 0644;
98
99
	expect_access(FUSE_ROOT_ID, X_OK, 0);
100
	expect_lookup(RELPATH, ino);
101
	expect_noaccess(ino, 0);
102
	EXPECT_CALL(*m_mock, process(
103
		ResultOf([](auto in) {
104
			return (in.header.opcode == FUSE_SETATTR &&
105
				in.header.nodeid == ino);
106
		}, Eq(true)),
107
		_)
108
	).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
109
		SET_OUT_HEADER_LEN(out, attr);
110
		out.body.attr.attr.ino = ino;	// Must match nodeid
111
		out.body.attr.attr.mode = S_IFREG | newmode;
112
	})));
113
114
	EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
115
}
116
117
/*
118
 * Create a new file
119
 *
120
 * There should never be a FUSE_ACCESS sent for this operation, except for
121
 * search permissions on the parent directory.
122
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
123
 */
124
TEST_F(Access, create)
125
{
126
	const char FULLPATH[] = "mountpoint/some_file.txt";
127
	const char RELPATH[] = "some_file.txt";
128
	mode_t mode = S_IFREG | 0755;
129
	uint64_t ino = 42;
130
131
	expect_access(FUSE_ROOT_ID, X_OK, 0);
132
	expect_noaccess(FUSE_ROOT_ID, R_OK | W_OK);
133
	//expect_lookup(RELPATH, ino);
134
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
135
		.WillOnce(Invoke(ReturnErrno(ENOENT)));
136
	expect_noaccess(ino, 0);
137
	EXPECT_CALL(*m_mock, process(
138
		ResultOf([=](auto in) {
139
			return (in.header.opcode == FUSE_CREATE);
140
		}, Eq(true)),
141
		_)
142
	).WillOnce(ReturnErrno(EPERM));
143
144
	EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, mode));
145
	EXPECT_EQ(EPERM, errno);
146
}
147
59
/* The error case of FUSE_ACCESS.  */
148
/* The error case of FUSE_ACCESS.  */
60
TEST_F(Access, eaccess)
149
TEST_F(Access, eaccess)
61
{
150
{
Lines 105-110 Link Here
105
	ASSERT_EQ(EROFS, errno);
194
	ASSERT_EQ(EROFS, errno);
106
}
195
}
107
196
197
198
/*
199
 * Lookup an extended attribute
200
 *
201
 * There should never be a FUSE_ACCESS sent for this operation, except possibly
202
 * during the lookup phase.
203
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
204
 */
205
TEST_F(Access, Getxattr)
206
{
207
	const char FULLPATH[] = "mountpoint/some_file.txt";
208
	const char RELPATH[] = "some_file.txt";
209
	uint64_t ino = 42;
210
	char data[80];
211
	int ns = EXTATTR_NAMESPACE_USER;
212
	ssize_t r;
213
214
	expect_access(FUSE_ROOT_ID, X_OK, 0);
215
	expect_lookup(RELPATH, ino);
216
	expect_noaccess(ino, 0);
217
	expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
218
219
	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
220
	ASSERT_EQ(-1, r);
221
	ASSERT_EQ(ENOATTR, errno);
222
}
223
108
/* The successful case of FUSE_ACCESS.  */
224
/* The successful case of FUSE_ACCESS.  */
109
TEST_F(Access, ok)
225
TEST_F(Access, ok)
110
{
226
{
Lines 119-121 Link Here
119
235
120
	ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
236
	ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
121
}
237
}
238
239
/*
240
 * Unlink a file
241
 *
242
 * There should never be a FUSE_ACCESS sent for this operation, except for
243
 * search permissions on the parent directory.
244
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
245
 */
246
TEST_F(Access, unlink)
247
{
248
	const char FULLPATH[] = "mountpoint/some_file.txt";
249
	const char RELPATH[] = "some_file.txt";
250
	uint64_t ino = 42;
251
252
	expect_access(FUSE_ROOT_ID, X_OK, 0);
253
	expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
254
	expect_noaccess(ino, 0);
255
	expect_lookup(RELPATH, ino);
256
	expect_unlink(1, RELPATH, EPERM);
257
258
	ASSERT_NE(0, unlink(FULLPATH));
259
	ASSERT_EQ(EPERM, errno);
260
}
261
262
/*
263
 * Lookup an extended attribute
264
 *
265
 * There should never be a FUSE_ACCESS sent for this operation, except possibly
266
 * during the lookup phase.
267
 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
268
 */
269
TEST_F(Access, unlink_sticky_directory)
270
{
271
	const char FULLPATH[] = "mountpoint/some_file.txt";
272
	const char RELPATH[] = "some_file.txt";
273
	uint64_t ino = 42;
274
275
	expect_access(FUSE_ROOT_ID, X_OK, 0);
276
	expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
277
	expect_noaccess(ino, 0);
278
	EXPECT_CALL(*m_mock, process(
279
		ResultOf([=](auto in) {
280
			return (in.header.opcode == FUSE_GETATTR &&
281
				in.header.nodeid == FUSE_ROOT_ID);
282
		}, Eq(true)),
283
		_)
284
	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out)
285
	{
286
		SET_OUT_HEADER_LEN(out, attr);
287
		out.body.attr.attr.ino = FUSE_ROOT_ID;
288
		out.body.attr.attr.mode = S_IFDIR | 01777;
289
		out.body.attr.attr.uid = 0;
290
		out.body.attr.attr_valid = UINT64_MAX;
291
	})));
292
	EXPECT_CALL(*m_mock, process(
293
		ResultOf([=](auto in) {
294
			return (in.header.opcode == FUSE_ACCESS &&
295
				in.header.nodeid == ino);
296
		}, Eq(true)),
297
		_)
298
	).Times(0);
299
	expect_lookup(RELPATH, ino);
300
	expect_unlink(FUSE_ROOT_ID, RELPATH, EPERM);
301
302
	ASSERT_EQ(-1, unlink(FULLPATH));
303
	ASSERT_EQ(EPERM, errno);
304
}
(-)tests/sys/fs/fusefs/default_permissions.cc (-1 / +1 lines)
Lines 125-131 Link Here
125
		out.body.attr.attr.mode = mode;
125
		out.body.attr.attr.mode = mode;
126
		out.body.attr.attr.size = 0;
126
		out.body.attr.attr.size = 0;
127
		out.body.attr.attr.uid = uid;
127
		out.body.attr.attr.uid = uid;
128
		out.body.attr.attr.uid = gid;
128
		out.body.attr.attr.gid = gid;
129
		out.body.attr.attr_valid = attr_valid;
129
		out.body.attr.attr_valid = attr_valid;
130
	})));
130
	})));
131
}
131
}
(-)tests/sys/fs/fusefs/lookup.cc (-1 / +26 lines)
Lines 39-45 Link Here
39
39
40
using namespace testing;
40
using namespace testing;
41
41
42
class Lookup: public FuseTest {};
42
class Lookup: public FuseTest {
43
public:
44
/* 
45
 * Expect a getattr of the mountpoint that may or may not happen.  It usually
46
 * doesn't, but it can, for example when mac_bsdextended.ko is loaded and
47
 * active.  In any case, it's allowed
48
 */
49
void maybe_expect_getattr()
50
{
51
	EXPECT_CALL(*m_mock, process(
52
		ResultOf([=](auto in) {
53
			return (in.header.opcode == FUSE_GETATTR &&
54
				in.header.nodeid == 1);
55
		}, Eq(true)),
56
		_)
57
	).Times(AtMost(1))
58
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
59
		SET_OUT_HEADER_LEN(out, attr);
60
		out.body.attr.attr.ino = 1;
61
		out.body.attr.attr.mode = S_IFDIR | 0755;
62
		out.body.attr.attr_valid = UINT64_MAX;
63
	})));
64
}
65
};
66
43
class Lookup_7_8: public Lookup {
67
class Lookup_7_8: public Lookup {
44
public:
68
public:
45
virtual void SetUp() {
69
virtual void SetUp() {
Lines 301-306 Link Here
301
	const char FULLPATH[] = "mountpoint/some_file.txt";
325
	const char FULLPATH[] = "mountpoint/some_file.txt";
302
	const char RELPATH[] = "some_file.txt";
326
	const char RELPATH[] = "some_file.txt";
303
327
328
	//maybe_expect_getattr();
304
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
329
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
305
	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
330
	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
306
		SET_OUT_HEADER_LEN(out, entry);
331
		SET_OUT_HEADER_LEN(out, entry);
(-)tests/sys/fs/fusefs/notify.cc (-11 / +100 lines)
Lines 323-334 Link Here
323
	EXPECT_EQ(0, (intptr_t)thr0_value);
323
	EXPECT_EQ(0, (intptr_t)thr0_value);
324
}
324
}
325
325
326
TEST_F(Notify, inval_inode_with_clean_cache)
326
/* Invalidate I/O in the pattern generated by cephfs */
327
TEST_F(Notify, inval_inode_like_cephfs)
327
{
328
{
328
	const static char FULLPATH[] = "mountpoint/foo";
329
	const static char FULLPATH[] = "mountpoint/foo";
329
	const static char RELPATH[] = "foo";
330
	const static char RELPATH[] = "foo";
330
	const char CONTENTS0[] = "abcdefgh";
331
	const char CONTENTS0[] = "0123456789";
331
	const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
332
	const char CONTENTS1[] = "0123456789Singapore";
332
	struct inval_inode_args iia;
333
	struct inval_inode_args iia;
333
	struct stat sb;
334
	struct stat sb;
334
	ino_t ino = 42;
335
	ino_t ino = 42;
Lines 343-348 Link Here
343
344
344
	expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
345
	expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
345
	expect_open(ino, 0, 1);
346
	expect_open(ino, 0, 1);
347
	expect_read(ino, 0, size0, size0, CONTENTS0);
348
349
	/* Fill the data cache */
350
	fd = open(FULLPATH, O_RDWR);
351
	ASSERT_LE(0, fd) << strerror(errno);
352
	ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno);
353
	EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
354
355
	/* Evict the data cache */
356
	iia.mock = m_mock;
357
	iia.ino = ino;
358
	iia.off = 0;
359
	iia.len = 0;
360
	ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
361
		<< strerror(errno);
362
	pthread_join(th0, &thr0_value);
363
	EXPECT_EQ(0, (intptr_t)thr0_value);
364
365
	/* cache attributes were purged; this will trigger a new GETATTR */
346
	EXPECT_CALL(*m_mock, process(
366
	EXPECT_CALL(*m_mock, process(
347
		ResultOf([=](auto in) {
367
		ResultOf([=](auto in) {
348
			return (in.header.opcode == FUSE_GETATTR &&
368
			return (in.header.opcode == FUSE_GETATTR &&
Lines 356-368 Link Here
356
		out.body.attr.attr.size = size1;
376
		out.body.attr.attr.size = size1;
357
		out.body.attr.attr.uid = uid;
377
		out.body.attr.attr.uid = uid;
358
	})));
378
	})));
359
	expect_read(ino, 0, size0, size0, CONTENTS0);
360
	expect_read(ino, 0, size1, size1, CONTENTS1);
379
	expect_read(ino, 0, size1, size1, CONTENTS1);
380
	ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
381
	EXPECT_EQ(uid, sb.st_uid);
382
	EXPECT_EQ(size1, sb.st_size);
361
383
384
	/* This read should not be serviced by cache */
385
	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
386
	ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
387
	EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
388
389
	leak(fd);
390
}
391
392
TEST_F(Notify, inval_inode_with_clean_cache)
393
{
394
	const static char FULLPATH[] = "mountpoint/foo";
395
	const static char RELPATH[] = "foo";
396
	const char CONTENTS0[] = "abcdefgh";
397
	const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
398
	struct inval_inode_args iia;
399
	struct stat sb;
400
	ino_t ino = 42;
401
	void *thr0_value;
402
	Sequence seq;
403
	uid_t uid = 12345;
404
	pthread_t th0;
405
	ssize_t size0 = sizeof(CONTENTS0);
406
	ssize_t size1 = sizeof(CONTENTS1);
407
	char buf[80];
408
	int fd;
409
410
	expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
411
	expect_open(ino, 0, 1);
412
	expect_read(ino, 0, size0, size0, CONTENTS0);
413
362
	/* Fill the data cache */
414
	/* Fill the data cache */
363
	fd = open(FULLPATH, O_RDWR);
415
	fd = open(FULLPATH, O_RDWR);
364
	ASSERT_LE(0, fd) << strerror(errno);
416
	ASSERT_LE(0, fd) << strerror(errno);
365
	ASSERT_EQ(size0, read(fd, buf, size0)) << strerror(errno);
417
	ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno);
366
	EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
418
	EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
367
419
368
	/* Evict the data cache */
420
	/* Evict the data cache */
Lines 376-381 Link Here
376
	EXPECT_EQ(0, (intptr_t)thr0_value);
428
	EXPECT_EQ(0, (intptr_t)thr0_value);
377
429
378
	/* cache attributes were purged; this will trigger a new GETATTR */
430
	/* cache attributes were purged; this will trigger a new GETATTR */
431
	EXPECT_CALL(*m_mock, process(
432
		ResultOf([=](auto in) {
433
			return (in.header.opcode == FUSE_GETATTR &&
434
				in.header.nodeid == ino);
435
		}, Eq(true)),
436
		_)
437
	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
438
		SET_OUT_HEADER_LEN(out, attr);
439
		out.body.attr.attr.mode = S_IFREG | 0644;
440
		out.body.attr.attr_valid = UINT64_MAX;
441
		out.body.attr.attr.size = size1;
442
		out.body.attr.attr.uid = uid;
443
	})));
444
	expect_read(ino, 0, size1, size1, CONTENTS1);
379
	ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
445
	ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
380
	EXPECT_EQ(uid, sb.st_uid);
446
	EXPECT_EQ(uid, sb.st_uid);
381
	EXPECT_EQ(size1, sb.st_size);
447
	EXPECT_EQ(size1, sb.st_size);
Lines 382-388 Link Here
382
448
383
	/* This read should not be serviced by cache */
449
	/* This read should not be serviced by cache */
384
	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
450
	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
385
	ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
451
	ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
386
	EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
452
	EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
387
453
388
	leak(fd);
454
	leak(fd);
Lines 441-447 Link Here
441
	EXPECT_EQ(0, (intptr_t)thr0_value);
507
	EXPECT_EQ(0, (intptr_t)thr0_value);
442
508
443
	/* This read should be serviced by cache */
509
	/* This read should be serviced by cache */
444
	ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
510
	ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
445
	EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
511
	EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
446
512
447
	leak(fd);
513
	leak(fd);
Lines 451-463 Link Here
451
{
517
{
452
	const static char FULLPATH[] = "mountpoint/foo";
518
	const static char FULLPATH[] = "mountpoint/foo";
453
	const static char RELPATH[] = "foo";
519
	const static char RELPATH[] = "foo";
454
	const char CONTENTS[] = "abcdefgh";
520
	const char CONTENTS0[] = "abcdefgh";
521
	const char CONTENTS1[] = "ijklmnop";
455
	struct inval_inode_args iia;
522
	struct inval_inode_args iia;
456
	ino_t ino = 42;
523
	ino_t ino = 42;
457
	void *thr0_value;
524
	void *thr0_value;
458
	Sequence seq;
525
	Sequence seq;
459
	pthread_t th0;
526
	pthread_t th0;
460
	ssize_t bufsize = sizeof(CONTENTS);
527
	ssize_t size0 = sizeof(CONTENTS0);
528
	ssize_t size1 = sizeof(CONTENTS1);
529
	char buf[80];
461
	int fd;
530
	int fd;
462
531
463
	expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
532
	expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
Lines 466-474 Link Here
466
	/* Fill the data cache */
535
	/* Fill the data cache */
467
	fd = open(FULLPATH, O_RDWR);
536
	fd = open(FULLPATH, O_RDWR);
468
	ASSERT_LE(0, fd);
537
	ASSERT_LE(0, fd);
469
	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
538
	ASSERT_EQ(size0, write(fd, CONTENTS0, size0)) << strerror(errno);
470
539
471
	expect_write(ino, 0, bufsize, CONTENTS);
540
	expect_write(ino, 0, size0, CONTENTS0);
472
	/* 
541
	/* 
473
	 * The FUSE protocol does not require an fsync here, but FreeBSD's
542
	 * The FUSE protocol does not require an fsync here, but FreeBSD's
474
	 * bufobj_invalbuf sends it anyway
543
	 * bufobj_invalbuf sends it anyway
Lines 485-490 Link Here
485
	pthread_join(th0, &thr0_value);
554
	pthread_join(th0, &thr0_value);
486
	EXPECT_EQ(0, (intptr_t)thr0_value);
555
	EXPECT_EQ(0, (intptr_t)thr0_value);
487
556
557
	/* 
558
	 * Reading again should trigger another FUSE_READ because the cache
559
	 * is empty.
560
	 */
561
	EXPECT_CALL(*m_mock, process(
562
		ResultOf([=](auto in) {
563
			return (in.header.opcode == FUSE_GETATTR &&
564
				in.header.nodeid == ino);
565
		}, Eq(true)),
566
		_)
567
	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
568
		SET_OUT_HEADER_LEN(out, attr);
569
		out.body.attr.attr.mode = S_IFREG | 0644;
570
		out.body.attr.attr_valid = UINT64_MAX;
571
		out.body.attr.attr.size = size1;
572
	})));
573
	expect_read(ino, 0, size1, size1, CONTENTS1);
574
	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
575
	ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
576
488
	leak(fd);
577
	leak(fd);
489
}
578
}
490
579
(-)tests/sys/fs/fusefs/read.cc (-1 / +1 lines)
Lines 828-834 Link Here
828
828
829
/* sendfile should fail gracefully if fuse declines the read */
829
/* sendfile should fail gracefully if fuse declines the read */
830
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
830
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
831
TEST_F(Read, sendfile_eio)
831
TEST_F(Read, DISABLED_sendfile_eio)
832
{
832
{
833
	const char FULLPATH[] = "mountpoint/some_file.txt";
833
	const char FULLPATH[] = "mountpoint/some_file.txt";
834
	const char RELPATH[] = "some_file.txt";
834
	const char RELPATH[] = "some_file.txt";
(-)tests/sys/fs/fusefs/rename.cc (-25 lines)
Lines 53-76 Link Here
53
53
54
		FuseTest::TearDown();
54
		FuseTest::TearDown();
55
	}
55
	}
56
57
	void expect_getattr(uint64_t ino, mode_t mode)
58
	{
59
		EXPECT_CALL(*m_mock, process(
60
			ResultOf([=](auto in) {
61
				return (in.header.opcode == FUSE_GETATTR &&
62
					in.header.nodeid == ino);
63
			}, Eq(true)),
64
			_)
65
		).WillOnce(Invoke(
66
			ReturnImmediate([=](auto i __unused, auto& out) {
67
			SET_OUT_HEADER_LEN(out, attr);
68
			out.body.attr.attr.ino = ino;	// Must match nodeid
69
			out.body.attr.attr.mode = mode;
70
			out.body.attr.attr_valid = UINT64_MAX;
71
		})));
72
	}
73
74
};
56
};
75
57
76
// EINVAL, dst is subdir of src
58
// EINVAL, dst is subdir of src
Lines 82-88 Link Here
82
	const char RELSRC[] = "src";
64
	const char RELSRC[] = "src";
83
	uint64_t src_ino = 42;
65
	uint64_t src_ino = 42;
84
66
85
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
86
	expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2);
67
	expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2);
87
	EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
68
	EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
88
69
Lines 123-129 Link Here
123
	 */
104
	 */
124
	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
105
	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
125
106
126
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
127
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
107
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
128
	/* LOOKUP returns a negative cache entry for dst */
108
	/* LOOKUP returns a negative cache entry for dst */
129
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
109
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
Lines 158-164 Link Here
158
	uint64_t ino = 42;
138
	uint64_t ino = 42;
159
	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
139
	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
160
140
161
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
162
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
141
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
163
	/* LOOKUP returns a negative cache entry for dst */
142
	/* LOOKUP returns a negative cache entry for dst */
164
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
143
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
Lines 196-202 Link Here
196
	tmpfd = mkstemp(tmpfile);
175
	tmpfd = mkstemp(tmpfile);
197
	ASSERT_LE(0, tmpfd) << strerror(errno);
176
	ASSERT_LE(0, tmpfd) << strerror(errno);
198
177
199
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
200
	expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2);
178
	expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2);
201
179
202
	ASSERT_NE(0, rename(tmpfile, FULLB));
180
	ASSERT_NE(0, rename(tmpfile, FULLB));
Lines 215-221 Link Here
215
	uint64_t dst_dir_ino = FUSE_ROOT_ID;
193
	uint64_t dst_dir_ino = FUSE_ROOT_ID;
216
	uint64_t ino = 42;
194
	uint64_t ino = 42;
217
195
218
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
219
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
196
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
220
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
197
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
221
	.WillOnce(Invoke(ReturnErrno(ENOENT)));
198
	.WillOnce(Invoke(ReturnErrno(ENOENT)));
Lines 251-257 Link Here
251
	struct stat sb;
228
	struct stat sb;
252
229
253
	expect_lookup(RELSRC, ino, S_IFDIR | 0755, 0, 1);
230
	expect_lookup(RELSRC, ino, S_IFDIR | 0755, 0, 1);
254
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
255
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDSTDIR)
231
	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDSTDIR)
256
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
232
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
257
		SET_OUT_HEADER_LEN(out, entry);
233
		SET_OUT_HEADER_LEN(out, entry);
Lines 303-309 Link Here
303
	uint64_t dst_dir_ino = FUSE_ROOT_ID;
279
	uint64_t dst_dir_ino = FUSE_ROOT_ID;
304
	uint64_t ino = 42;
280
	uint64_t ino = 42;
305
281
306
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
307
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
282
	expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
308
	expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1);
283
	expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1);
309
	EXPECT_CALL(*m_mock, process(
284
	EXPECT_CALL(*m_mock, process(
(-)tests/sys/fs/fusefs/rmdir.cc (-24 / +14 lines)
Lines 42-63 Link Here
42
42
43
class Rmdir: public FuseTest {
43
class Rmdir: public FuseTest {
44
public:
44
public:
45
void expect_getattr(uint64_t ino, mode_t mode)
46
{
47
	EXPECT_CALL(*m_mock, process(
48
		ResultOf([=](auto in) {
49
			return (in.header.opcode == FUSE_GETATTR &&
50
				in.header.nodeid == ino);
51
		}, Eq(true)),
52
		_)
53
	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
54
		SET_OUT_HEADER_LEN(out, attr);
55
		out.body.attr.attr.ino = ino;	// Must match nodeid
56
		out.body.attr.attr.mode = mode;
57
		out.body.attr.attr_valid = UINT64_MAX;
58
	})));
59
}
60
61
void expect_lookup(const char *relpath, uint64_t ino, int times=1)
45
void expect_lookup(const char *relpath, uint64_t ino, int times=1)
62
{
46
{
63
	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
47
	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
Lines 95-119 Link Here
95
	struct stat sb;
79
	struct stat sb;
96
	sem_t sem;
80
	sem_t sem;
97
	uint64_t ino = 42;
81
	uint64_t ino = 42;
82
	Sequence seq;
98
83
99
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
84
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
100
85
86
	expect_lookup(RELPATH, ino);
101
	EXPECT_CALL(*m_mock, process(
87
	EXPECT_CALL(*m_mock, process(
102
		ResultOf([=](auto in) {
88
		ResultOf([=](auto in) {
89
			return (in.header.opcode == FUSE_RMDIR &&
90
				0 == strcmp(RELPATH, in.body.rmdir) &&
91
				in.header.nodeid == FUSE_ROOT_ID);
92
		}, Eq(true)),
93
		_)
94
	).InSequence(seq)
95
	.WillOnce(Invoke(ReturnErrno(0)));
96
	expect_forget(ino, 1, &sem);
97
	EXPECT_CALL(*m_mock, process(
98
		ResultOf([=](auto in) {
103
			return (in.header.opcode == FUSE_GETATTR &&
99
			return (in.header.opcode == FUSE_GETATTR &&
104
				in.header.nodeid == FUSE_ROOT_ID);
100
				in.header.nodeid == FUSE_ROOT_ID);
105
		}, Eq(true)),
101
		}, Eq(true)),
106
		_)
102
		_)
107
	).Times(2)
103
	).InSequence(seq)
108
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
104
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
109
		SET_OUT_HEADER_LEN(out, attr);
105
		SET_OUT_HEADER_LEN(out, attr);
110
		out.body.attr.attr.ino = ino;	// Must match nodeid
106
		out.body.attr.attr.ino = FUSE_ROOT_ID;
111
		out.body.attr.attr.mode = S_IFDIR | 0755;
107
		out.body.attr.attr.mode = S_IFDIR | 0755;
112
		out.body.attr.attr_valid = UINT64_MAX;
108
		out.body.attr.attr_valid = UINT64_MAX;
113
	})));
109
	})));
114
	expect_lookup(RELPATH, ino);
115
	expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
116
	expect_forget(ino, 1, &sem);
117
110
118
	ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
111
	ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
119
	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
112
	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
Lines 127-133 Link Here
127
	const char RELPATH[] = "some_dir";
120
	const char RELPATH[] = "some_dir";
128
	uint64_t ino = 42;
121
	uint64_t ino = 42;
129
122
130
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
131
	expect_lookup(RELPATH, ino);
123
	expect_lookup(RELPATH, ino);
132
	expect_rmdir(FUSE_ROOT_ID, RELPATH, ENOTEMPTY);
124
	expect_rmdir(FUSE_ROOT_ID, RELPATH, ENOTEMPTY);
133
125
Lines 143-149 Link Here
143
	sem_t sem;
135
	sem_t sem;
144
	uint64_t ino = 42;
136
	uint64_t ino = 42;
145
137
146
	expect_getattr(1, S_IFDIR | 0755);
147
	expect_lookup(RELPATH, ino, 2);
138
	expect_lookup(RELPATH, ino, 2);
148
	expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
139
	expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
149
	expect_forget(ino, 1, &sem);
140
	expect_forget(ino, 1, &sem);
Lines 163-169 Link Here
163
154
164
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
155
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
165
156
166
	expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
167
	expect_lookup(RELPATH, ino);
157
	expect_lookup(RELPATH, ino);
168
	expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
158
	expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
169
	expect_forget(ino, 1, &sem);
159
	expect_forget(ino, 1, &sem);
(-)tests/sys/fs/fusefs/unlink.cc (-27 / +14 lines)
Lines 41-62 Link Here
41
41
42
class Unlink: public FuseTest {
42
class Unlink: public FuseTest {
43
public:
43
public:
44
void expect_getattr(uint64_t ino, mode_t mode)
45
{
46
	EXPECT_CALL(*m_mock, process(
47
		ResultOf([=](auto in) {
48
			return (in.header.opcode == FUSE_GETATTR &&
49
				in.header.nodeid == ino);
50
		}, Eq(true)),
51
		_)
52
	).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
53
		SET_OUT_HEADER_LEN(out, attr);
54
		out.body.attr.attr.ino = ino;	// Must match nodeid
55
		out.body.attr.attr.mode = mode;
56
		out.body.attr.attr_valid = UINT64_MAX;
57
	})));
58
}
59
60
void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1)
44
void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1)
61
{
45
{
62
	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
46
	EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
Lines 89-95 Link Here
89
	struct stat sb_old, sb_new;
73
	struct stat sb_old, sb_new;
90
	int fd1;
74
	int fd1;
91
75
92
	expect_getattr(1, S_IFDIR | 0755);
93
	expect_lookup(RELPATH0, ino, 1, 2);
76
	expect_lookup(RELPATH0, ino, 1, 2);
94
	expect_lookup(RELPATH1, ino, 1, 2);
77
	expect_lookup(RELPATH1, ino, 1, 2);
95
	expect_open(ino, 0, 1);
78
	expect_open(ino, 0, 1);
Lines 117-139 Link Here
117
	const char RELPATH[] = "some_file.txt";
100
	const char RELPATH[] = "some_file.txt";
118
	struct stat sb;
101
	struct stat sb;
119
	uint64_t ino = 42;
102
	uint64_t ino = 42;
103
	Sequence seq;
120
104
105
	/* Use nlink=2 so we don't get a FUSE_FORGET */
106
	expect_lookup(RELPATH, ino, 1, 2);
121
	EXPECT_CALL(*m_mock, process(
107
	EXPECT_CALL(*m_mock, process(
122
		ResultOf([=](auto in) {
108
		ResultOf([=](auto in) {
109
			return (in.header.opcode == FUSE_UNLINK &&
110
				0 == strcmp(RELPATH, in.body.unlink) &&
111
				in.header.nodeid == FUSE_ROOT_ID);
112
		}, Eq(true)),
113
		_)
114
	).InSequence(seq)
115
	.WillOnce(Invoke(ReturnErrno(0)));
116
	EXPECT_CALL(*m_mock, process(
117
		ResultOf([=](auto in) {
123
			return (in.header.opcode == FUSE_GETATTR &&
118
			return (in.header.opcode == FUSE_GETATTR &&
124
				in.header.nodeid == FUSE_ROOT_ID);
119
				in.header.nodeid == FUSE_ROOT_ID);
125
		}, Eq(true)),
120
		}, Eq(true)),
126
		_)
121
		_)
127
	).Times(2)
122
	).InSequence(seq)
128
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
123
	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
129
		SET_OUT_HEADER_LEN(out, attr);
124
		SET_OUT_HEADER_LEN(out, attr);
130
		out.body.attr.attr.ino = ino;	// Must match nodeid
125
		out.body.attr.attr.ino = FUSE_ROOT_ID;
131
		out.body.attr.attr.mode = S_IFDIR | 0755;
126
		out.body.attr.attr.mode = S_IFDIR | 0755;
132
		out.body.attr.attr_valid = UINT64_MAX;
127
		out.body.attr.attr_valid = UINT64_MAX;
133
	})));
128
	})));
134
	/* Use nlink=2 so we don't get a FUSE_FORGET */
135
	expect_lookup(RELPATH, ino, 1, 2);
136
	expect_unlink(1, RELPATH, 0);
137
129
138
	ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
130
	ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
139
	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
131
	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
Lines 145-151 Link Here
145
	const char RELPATH[] = "some_file.txt";
137
	const char RELPATH[] = "some_file.txt";
146
	uint64_t ino = 42;
138
	uint64_t ino = 42;
147
139
148
	expect_getattr(1, S_IFDIR | 0755);
149
	expect_lookup(RELPATH, ino, 1);
140
	expect_lookup(RELPATH, ino, 1);
150
	expect_unlink(1, RELPATH, EPERM);
141
	expect_unlink(1, RELPATH, EPERM);
151
142
Lines 162-168 Link Here
162
	const char RELPATH[] = "some_file.txt";
153
	const char RELPATH[] = "some_file.txt";
163
	uint64_t ino = 42;
154
	uint64_t ino = 42;
164
155
165
	expect_getattr(1, S_IFDIR | 0755);
166
	expect_lookup(RELPATH, ino, 2, 2);
156
	expect_lookup(RELPATH, ino, 2, 2);
167
	expect_unlink(1, RELPATH, 0);
157
	expect_unlink(1, RELPATH, 0);
168
158
Lines 182-188 Link Here
182
	const char RELPATH1[] = "other_file.txt";
172
	const char RELPATH1[] = "other_file.txt";
183
	uint64_t ino = 42;
173
	uint64_t ino = 42;
184
174
185
	expect_getattr(1, S_IFDIR | 0755);
186
	expect_lookup(RELPATH0, ino, 1, 2);
175
	expect_lookup(RELPATH0, ino, 1, 2);
187
	expect_unlink(1, RELPATH0, 0);
176
	expect_unlink(1, RELPATH0, 0);
188
	EXPECT_CALL(*m_mock, process(
177
	EXPECT_CALL(*m_mock, process(
Lines 213-219 Link Here
213
202
214
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
203
	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
215
204
216
	expect_getattr(1, S_IFDIR | 0755);
217
	expect_lookup(RELPATH, ino, 1);
205
	expect_lookup(RELPATH, ino, 1);
218
	expect_unlink(1, RELPATH, 0);
206
	expect_unlink(1, RELPATH, 0);
219
	expect_forget(ino, 1, &sem);
207
	expect_forget(ino, 1, &sem);
Lines 233-239 Link Here
233
	uint64_t ino = 42;
221
	uint64_t ino = 42;
234
	int fd;
222
	int fd;
235
223
236
	expect_getattr(1, S_IFDIR | 0755);
237
	expect_lookup(RELPATH0, ino, 2);
224
	expect_lookup(RELPATH0, ino, 2);
238
	expect_open(ino, 0, 1);
225
	expect_open(ino, 0, 1);
239
	expect_unlink(1, RELPATH0, 0);
226
	expect_unlink(1, RELPATH0, 0);
(-)tests/sys/fs/fusefs/utils.cc (+14 lines)
Lines 273-278 Link Here
273
	})));
273
	})));
274
}
274
}
275
275
276
void FuseTest::expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r)
277
{
278
	EXPECT_CALL(*m_mock, process(
279
		ResultOf([=](auto in) {
280
			const char *a = (const char*)in.body.bytes +
281
				sizeof(fuse_getxattr_in);
282
			return (in.header.opcode == FUSE_GETXATTR &&
283
				in.header.nodeid == ino &&
284
				0 == strcmp(attr, a));
285
		}, Eq(true)),
286
		_)
287
	).WillOnce(Invoke(r));
288
}
289
276
void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
290
void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
277
	uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
291
	uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
278
{
292
{
(-)tests/sys/fs/fusefs/utils.hh (+6 lines)
Lines 133-138 Link Here
133
	void expect_getattr(uint64_t ino, uint64_t size);
133
	void expect_getattr(uint64_t ino, uint64_t size);
134
134
135
	/*
135
	/*
136
	 * Create an expectation that FUSE_GETXATTR will be called once for the
137
	 * given inode.
138
	 */
139
	void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r);
140
141
	/*
136
	 * Create an expectation that FUSE_LOOKUP will be called for the given
142
	 * Create an expectation that FUSE_LOOKUP will be called for the given
137
	 * path exactly times times and cache validity period.  It will respond
143
	 * path exactly times times and cache validity period.  It will respond
138
	 * with inode ino, mode mode, filesize size.
144
	 * with inode ino, mode mode, filesize size.
(-)tests/sys/fs/fusefs/xattr.cc (-14 lines)
Lines 62-81 Link Here
62
62
63
class Xattr: public FuseTest {
63
class Xattr: public FuseTest {
64
public:
64
public:
65
void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r)
66
{
67
	EXPECT_CALL(*m_mock, process(
68
		ResultOf([=](auto in) {
69
			const char *a = (const char*)in.body.bytes +
70
				sizeof(fuse_getxattr_in);
71
			return (in.header.opcode == FUSE_GETXATTR &&
72
				in.header.nodeid == ino &&
73
				0 == strcmp(attr, a));
74
		}, Eq(true)),
75
		_)
76
	).WillOnce(Invoke(r));
77
}
78
79
void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
65
void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
80
    Sequence *seq = NULL)
66
    Sequence *seq = NULL)
81
{
67
{

Return to bug 245689