xref: /freebsd/tests/sys/fs/fusefs/create.cc (revision 002e54b0aa8f651012fced4223b4cad484f2d08d)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39821f1d3SAlan Somers  *
49821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers  *
69821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers  * from the FreeBSD Foundation.
89821f1d3SAlan Somers  *
99821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers  * are met:
129821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers  *
189821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers  * SUCH DAMAGE.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <fcntl.h>
33ede571e4SAlan Somers #include <sys/socket.h>
34ede571e4SAlan Somers #include <sys/un.h>
359821f1d3SAlan Somers }
369821f1d3SAlan Somers 
379821f1d3SAlan Somers #include "mockfs.hh"
389821f1d3SAlan Somers #include "utils.hh"
399821f1d3SAlan Somers 
409821f1d3SAlan Somers using namespace testing;
419821f1d3SAlan Somers 
422d445be1SAlan Somers class Create: public FuseTest {
432d445be1SAlan Somers public:
442d445be1SAlan Somers 
45ede571e4SAlan Somers void expect_create(const char *relpath, mode_t mode, ProcessMockerT r)
462d445be1SAlan Somers {
472d445be1SAlan Somers 	EXPECT_CALL(*m_mock, process(
482d445be1SAlan Somers 		ResultOf([=](auto in) {
492d445be1SAlan Somers 			const char *name = (const char*)in->body.bytes +
502d445be1SAlan Somers 				sizeof(fuse_open_in);
512d445be1SAlan Somers 			return (in->header.opcode == FUSE_CREATE &&
52ede571e4SAlan Somers 				in->body.open.mode == mode &&
532d445be1SAlan Somers 				(0 == strcmp(relpath, name)));
542d445be1SAlan Somers 		}, Eq(true)),
552d445be1SAlan Somers 		_)
562d445be1SAlan Somers 	).WillOnce(Invoke(r));
572d445be1SAlan Somers }
582d445be1SAlan Somers 
592d445be1SAlan Somers };
609821f1d3SAlan Somers 
619821f1d3SAlan Somers /*
62*002e54b0SAlan Somers  * If FUSE_CREATE sets attr_valid, then subsequent GETATTRs should use the
639821f1d3SAlan Somers  * attribute cache
649821f1d3SAlan Somers  */
65cad67791SAlan Somers TEST_F(Create, attr_cache)
669821f1d3SAlan Somers {
679821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
689821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
69ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
709821f1d3SAlan Somers 	uint64_t ino = 42;
719821f1d3SAlan Somers 	int fd;
729821f1d3SAlan Somers 
739821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
74ede571e4SAlan Somers 	expect_create(RELPATH, mode,
75ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
769821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
77ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
789821f1d3SAlan Somers 		out->body.create.entry.nodeid = ino;
799821f1d3SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
809821f1d3SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
812d445be1SAlan Somers 	}));
829821f1d3SAlan Somers 
839821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
849821f1d3SAlan Somers 		ResultOf([=](auto in) {
859821f1d3SAlan Somers 			return (in->header.opcode == FUSE_GETATTR &&
869821f1d3SAlan Somers 				in->header.nodeid == ino);
879821f1d3SAlan Somers 		}, Eq(true)),
889821f1d3SAlan Somers 		_)
899821f1d3SAlan Somers 	).Times(0);
909821f1d3SAlan Somers 
919821f1d3SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
929821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
939821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
949821f1d3SAlan Somers }
959821f1d3SAlan Somers 
96*002e54b0SAlan Somers /* A successful CREATE operation should purge the parent dir's attr cache */
97*002e54b0SAlan Somers TEST_F(Create, clear_attr_cache)
98*002e54b0SAlan Somers {
99*002e54b0SAlan Somers 	const char FULLPATH[] = "mountpoint/src";
100*002e54b0SAlan Somers 	const char RELPATH[] = "src";
101*002e54b0SAlan Somers 	mode_t mode = S_IFREG | 0755;
102*002e54b0SAlan Somers 	uint64_t ino = 42;
103*002e54b0SAlan Somers 	int fd;
104*002e54b0SAlan Somers 	struct stat sb;
105*002e54b0SAlan Somers 
106*002e54b0SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
107*002e54b0SAlan Somers 	EXPECT_CALL(*m_mock, process(
108*002e54b0SAlan Somers 		ResultOf([=](auto in) {
109*002e54b0SAlan Somers 			return (in->header.opcode == FUSE_GETATTR &&
110*002e54b0SAlan Somers 				in->header.nodeid == 1);
111*002e54b0SAlan Somers 		}, Eq(true)),
112*002e54b0SAlan Somers 		_)
113*002e54b0SAlan Somers 	).Times(2)
114*002e54b0SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
115*002e54b0SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
116*002e54b0SAlan Somers 		out->body.attr.attr.ino = 1;
117*002e54b0SAlan Somers 		out->body.attr.attr.mode = S_IFDIR | 0755;
118*002e54b0SAlan Somers 		out->body.attr.attr_valid = UINT64_MAX;
119*002e54b0SAlan Somers 	})));
120*002e54b0SAlan Somers 
121*002e54b0SAlan Somers 	expect_create(RELPATH, mode,
122*002e54b0SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
123*002e54b0SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
124*002e54b0SAlan Somers 		out->body.create.entry.attr.mode = mode;
125*002e54b0SAlan Somers 		out->body.create.entry.nodeid = ino;
126*002e54b0SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
127*002e54b0SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
128*002e54b0SAlan Somers 	}));
129*002e54b0SAlan Somers 
130*002e54b0SAlan Somers 	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
131*002e54b0SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
132*002e54b0SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
133*002e54b0SAlan Somers 	EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
134*002e54b0SAlan Somers 
135*002e54b0SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
136*002e54b0SAlan Somers }
137*002e54b0SAlan Somers 
1389821f1d3SAlan Somers /*
1399821f1d3SAlan Somers  * The fuse daemon fails the request with EEXIST.  This usually indicates a
1409821f1d3SAlan Somers  * race condition: some other FUSE client created the file in between when the
1419821f1d3SAlan Somers  * kernel checked for it with lookup and tried to create it with create
1429821f1d3SAlan Somers  */
1439821f1d3SAlan Somers TEST_F(Create, eexist)
1449821f1d3SAlan Somers {
1459821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1469821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
147ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
1489821f1d3SAlan Somers 
1499821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
150ede571e4SAlan Somers 	expect_create(RELPATH, mode, ReturnErrno(EEXIST));
1519821f1d3SAlan Somers 	EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode));
1529821f1d3SAlan Somers 	EXPECT_EQ(EEXIST, errno);
1539821f1d3SAlan Somers }
1549821f1d3SAlan Somers 
1559821f1d3SAlan Somers /*
1569821f1d3SAlan Somers  * If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback
1579821f1d3SAlan Somers  * to FUSE_MKNOD/FUSE_OPEN
1589821f1d3SAlan Somers  */
15919ef317dSAlan Somers TEST_F(Create, Enosys)
1609821f1d3SAlan Somers {
1619821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1629821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
163ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
1649821f1d3SAlan Somers 	uint64_t ino = 42;
1659821f1d3SAlan Somers 	int fd;
1669821f1d3SAlan Somers 
1679821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
168ede571e4SAlan Somers 	expect_create(RELPATH, mode, ReturnErrno(ENOSYS));
1699821f1d3SAlan Somers 
1709821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1719821f1d3SAlan Somers 		ResultOf([=](auto in) {
1729821f1d3SAlan Somers 			const char *name = (const char*)in->body.bytes +
1739821f1d3SAlan Somers 				sizeof(fuse_mknod_in);
1749821f1d3SAlan Somers 			return (in->header.opcode == FUSE_MKNOD &&
1759821f1d3SAlan Somers 				in->body.mknod.mode == (S_IFREG | mode) &&
1769821f1d3SAlan Somers 				in->body.mknod.rdev == 0 &&
1779821f1d3SAlan Somers 				(0 == strcmp(RELPATH, name)));
1789821f1d3SAlan Somers 		}, Eq(true)),
1799821f1d3SAlan Somers 		_)
1809821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
18119ef317dSAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
182ede571e4SAlan Somers 		out->body.entry.attr.mode = mode;
18319ef317dSAlan Somers 		out->body.entry.nodeid = ino;
18419ef317dSAlan Somers 		out->body.entry.entry_valid = UINT64_MAX;
18519ef317dSAlan Somers 		out->body.entry.attr_valid = UINT64_MAX;
1869821f1d3SAlan Somers 	})));
1879821f1d3SAlan Somers 
1889821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1899821f1d3SAlan Somers 		ResultOf([=](auto in) {
1909821f1d3SAlan Somers 			return (in->header.opcode == FUSE_OPEN &&
1919821f1d3SAlan Somers 				in->header.nodeid == ino);
1929821f1d3SAlan Somers 		}, Eq(true)),
1939821f1d3SAlan Somers 		_)
1949821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) {
1959821f1d3SAlan Somers 		out->header.len = sizeof(out->header);
1969821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, open);
1979821f1d3SAlan Somers 	})));
1989821f1d3SAlan Somers 
1999821f1d3SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
2009821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2019821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2029821f1d3SAlan Somers }
2039821f1d3SAlan Somers 
2049821f1d3SAlan Somers /*
2059821f1d3SAlan Somers  * Creating a new file after FUSE_LOOKUP returned a negative cache entry
2069821f1d3SAlan Somers  */
2076248288eSAlan Somers TEST_F(Create, entry_cache_negative)
2089821f1d3SAlan Somers {
2099821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2109821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
211ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
2129821f1d3SAlan Somers 	uint64_t ino = 42;
2139821f1d3SAlan Somers 	int fd;
2149821f1d3SAlan Somers 	/*
2159821f1d3SAlan Somers 	 * Set entry_valid = 0 because this test isn't concerned with whether
2169821f1d3SAlan Somers 	 * or not we actually cache negative entries, only with whether we
2179821f1d3SAlan Somers 	 * interpret negative cache responses correctly.
2189821f1d3SAlan Somers 	 */
2199821f1d3SAlan Somers 	struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
2209821f1d3SAlan Somers 
2219821f1d3SAlan Somers 	/* create will first do a LOOKUP, adding a negative cache entry */
2229821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid));
223ede571e4SAlan Somers 	expect_create(RELPATH, mode,
224ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
2259821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
226ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
2279821f1d3SAlan Somers 		out->body.create.entry.nodeid = ino;
2289821f1d3SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
2299821f1d3SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
2302d445be1SAlan Somers 	}));
2319821f1d3SAlan Somers 
2329821f1d3SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
2339821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2349821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2359821f1d3SAlan Somers }
2369821f1d3SAlan Somers 
2379821f1d3SAlan Somers /*
2389821f1d3SAlan Somers  * Creating a new file should purge any negative namecache entries
2399821f1d3SAlan Somers  */
2406248288eSAlan Somers TEST_F(Create, entry_cache_negative_purge)
2419821f1d3SAlan Somers {
2429821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2439821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
244ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
2459821f1d3SAlan Somers 	uint64_t ino = 42;
2469821f1d3SAlan Somers 	int fd;
2479821f1d3SAlan Somers 	struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
2489821f1d3SAlan Somers 
2499821f1d3SAlan Somers 	/* create will first do a LOOKUP, adding a negative cache entry */
2509821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).Times(1)
2519821f1d3SAlan Somers 	.WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
2529821f1d3SAlan Somers 	.RetiresOnSaturation();
2539821f1d3SAlan Somers 
2549821f1d3SAlan Somers 	/* Then the CREATE should purge the negative cache entry */
255ede571e4SAlan Somers 	expect_create(RELPATH, mode,
256ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
2579821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
258ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
2599821f1d3SAlan Somers 		out->body.create.entry.nodeid = ino;
2609821f1d3SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
2612d445be1SAlan Somers 	}));
2629821f1d3SAlan Somers 
2639821f1d3SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
2649821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2659821f1d3SAlan Somers 
2669821f1d3SAlan Somers 	/* Finally, a subsequent lookup should query the daemon */
2679821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | mode, 0, 1);
2689821f1d3SAlan Somers 
2699821f1d3SAlan Somers 	ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
2709821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2719821f1d3SAlan Somers }
2729821f1d3SAlan Somers 
2739821f1d3SAlan Somers /*
2749821f1d3SAlan Somers  * The daemon is responsible for checking file permissions (unless the
2759821f1d3SAlan Somers  * default_permissions mount option was used)
2769821f1d3SAlan Somers  */
2779821f1d3SAlan Somers TEST_F(Create, eperm)
2789821f1d3SAlan Somers {
2799821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2809821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
281ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
2829821f1d3SAlan Somers 
2839821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
284ede571e4SAlan Somers 	expect_create(RELPATH, mode, ReturnErrno(EPERM));
2859821f1d3SAlan Somers 
2869821f1d3SAlan Somers 	EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode));
2879821f1d3SAlan Somers 	EXPECT_EQ(EPERM, errno);
2889821f1d3SAlan Somers }
2899821f1d3SAlan Somers 
2909821f1d3SAlan Somers TEST_F(Create, ok)
2919821f1d3SAlan Somers {
2929821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2939821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
294ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0755;
2959821f1d3SAlan Somers 	uint64_t ino = 42;
2969821f1d3SAlan Somers 	int fd;
2979821f1d3SAlan Somers 
2989821f1d3SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
299ede571e4SAlan Somers 	expect_create(RELPATH, mode,
300ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
3019821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
302ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
3039821f1d3SAlan Somers 		out->body.create.entry.nodeid = ino;
3049821f1d3SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
3059821f1d3SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
3062d445be1SAlan Somers 	}));
3079821f1d3SAlan Somers 
3089821f1d3SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
3099821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3109821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
3119821f1d3SAlan Somers }
3122d445be1SAlan Somers 
313ede571e4SAlan Somers /* Create a unix-domain socket */
314ede571e4SAlan Somers TEST_F(Create, socket)
315ede571e4SAlan Somers {
316ede571e4SAlan Somers 	const char FULLPATH[] = "mountpoint/some_sock";
317ede571e4SAlan Somers 	const char RELPATH[] = "some_sock";
318ede571e4SAlan Somers 	mode_t mode = S_IFSOCK | 0755;
319ede571e4SAlan Somers 	struct sockaddr_un sa;
320ede571e4SAlan Somers 	uint64_t ino = 42;
321ede571e4SAlan Somers 	int fd;
322ede571e4SAlan Somers 
323ede571e4SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
324ede571e4SAlan Somers 	expect_create(RELPATH, mode,
325ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
326ede571e4SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
327ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
328ede571e4SAlan Somers 		out->body.create.entry.nodeid = ino;
329ede571e4SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
330ede571e4SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
331ede571e4SAlan Somers 	}));
332ede571e4SAlan Somers 
333ede571e4SAlan Somers 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
334ede571e4SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
335ede571e4SAlan Somers 	sa.sun_family = AF_UNIX;
336ede571e4SAlan Somers 	strlcpy(sa.sun_path, FULLPATH, sizeof(sa.sun_path));
337ede571e4SAlan Somers 	ASSERT_EQ(0, bind(fd, (struct sockaddr*)&sa, sizeof(sa)))
338ede571e4SAlan Somers 		<< strerror(errno);
339ede571e4SAlan Somers }
340ede571e4SAlan Somers 
3412d445be1SAlan Somers /*
3422d445be1SAlan Somers  * A regression test for a bug that affected old FUSE implementations:
3432d445be1SAlan Somers  * open(..., O_WRONLY | O_CREAT, 0444) should work despite the seeming
3442d445be1SAlan Somers  * contradiction between O_WRONLY and 0444
3452d445be1SAlan Somers  *
3462d445be1SAlan Somers  * For example:
3472d445be1SAlan Somers  * https://bugs.launchpad.net/ubuntu/+source/sshfs-fuse/+bug/44886
3482d445be1SAlan Somers  */
3492d445be1SAlan Somers TEST_F(Create, wronly_0444)
3502d445be1SAlan Somers {
3512d445be1SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3522d445be1SAlan Somers 	const char RELPATH[] = "some_file.txt";
353ede571e4SAlan Somers 	mode_t mode = S_IFREG | 0444;
3542d445be1SAlan Somers 	uint64_t ino = 42;
3552d445be1SAlan Somers 	int fd;
3562d445be1SAlan Somers 
3572d445be1SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
358ede571e4SAlan Somers 	expect_create(RELPATH, mode,
359ede571e4SAlan Somers 		ReturnImmediate([=](auto in __unused, auto out) {
3602d445be1SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
361ede571e4SAlan Somers 		out->body.create.entry.attr.mode = mode;
3622d445be1SAlan Somers 		out->body.create.entry.nodeid = ino;
3632d445be1SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
3642d445be1SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
3652d445be1SAlan Somers 	}));
3662d445be1SAlan Somers 
3672d445be1SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_WRONLY, mode);
3682d445be1SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3692d445be1SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
3702d445be1SAlan Somers }
371