xref: /freebsd/tests/sys/fs/fusefs/default_permissions.cc (revision ff4fbdf548c152be65e7f209b2ad8966d238ca2c)
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 /*
329821f1d3SAlan Somers  * Tests for the "default_permissions" mount option.  They must be in their own
339821f1d3SAlan Somers  * file so they can be run as an unprivileged user
349821f1d3SAlan Somers  */
359821f1d3SAlan Somers 
369821f1d3SAlan Somers extern "C" {
37*ff4fbdf5SAlan Somers #include <sys/types.h>
38*ff4fbdf5SAlan Somers #include <sys/extattr.h>
39*ff4fbdf5SAlan Somers 
409821f1d3SAlan Somers #include <fcntl.h>
419821f1d3SAlan Somers #include <unistd.h>
429821f1d3SAlan Somers }
439821f1d3SAlan Somers 
449821f1d3SAlan Somers #include "mockfs.hh"
459821f1d3SAlan Somers #include "utils.hh"
469821f1d3SAlan Somers 
479821f1d3SAlan Somers using namespace testing;
489821f1d3SAlan Somers 
499821f1d3SAlan Somers class DefaultPermissions: public FuseTest {
509821f1d3SAlan Somers 
519821f1d3SAlan Somers virtual void SetUp() {
52*ff4fbdf5SAlan Somers 	m_default_permissions = true;
539821f1d3SAlan Somers 	FuseTest::SetUp();
54*ff4fbdf5SAlan Somers 	if (HasFatalFailure() || IsSkipped())
55*ff4fbdf5SAlan Somers 		return;
569821f1d3SAlan Somers 
579821f1d3SAlan Somers 	if (geteuid() == 0) {
589821f1d3SAlan Somers 		GTEST_SKIP() << "This test requires an unprivileged user";
599821f1d3SAlan Somers 	}
60*ff4fbdf5SAlan Somers 
61*ff4fbdf5SAlan Somers 	/* With -o default_permissions, FUSE_ACCESS should never be called */
62*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
63*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
64*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_ACCESS);
65*ff4fbdf5SAlan Somers 		}, Eq(true)),
66*ff4fbdf5SAlan Somers 		_)
67*ff4fbdf5SAlan Somers 	).Times(0);
689821f1d3SAlan Somers }
699821f1d3SAlan Somers 
709821f1d3SAlan Somers public:
71*ff4fbdf5SAlan Somers void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times,
72*ff4fbdf5SAlan Somers 	uid_t uid = 0)
739821f1d3SAlan Somers {
74*ff4fbdf5SAlan Somers 	/* Until the attr cache is working, we may send an additional GETATTR */
75*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
76*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
77*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_GETATTR &&
78*ff4fbdf5SAlan Somers 				in->header.nodeid == ino);
79*ff4fbdf5SAlan Somers 		}, Eq(true)),
80*ff4fbdf5SAlan Somers 		_)
81*ff4fbdf5SAlan Somers 	).Times(times)
82*ff4fbdf5SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
83*ff4fbdf5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
84*ff4fbdf5SAlan Somers 		out->body.attr.attr.ino = ino;	// Must match nodeid
85*ff4fbdf5SAlan Somers 		out->body.attr.attr.mode = mode;
86*ff4fbdf5SAlan Somers 		out->body.attr.attr.size = 0;
87*ff4fbdf5SAlan Somers 		out->body.attr.attr.uid = uid;
88*ff4fbdf5SAlan Somers 		out->body.attr.attr_valid = attr_valid;
89*ff4fbdf5SAlan Somers 	})));
90*ff4fbdf5SAlan Somers }
91*ff4fbdf5SAlan Somers 
92*ff4fbdf5SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
93*ff4fbdf5SAlan Somers 	uint64_t attr_valid, uid_t uid = 0)
94*ff4fbdf5SAlan Somers {
95*ff4fbdf5SAlan Somers 	FuseTest::expect_lookup(relpath, ino, mode, 0, 1, attr_valid, uid);
969821f1d3SAlan Somers }
979821f1d3SAlan Somers 
989821f1d3SAlan Somers };
999821f1d3SAlan Somers 
1009821f1d3SAlan Somers class Access: public DefaultPermissions {};
101*ff4fbdf5SAlan Somers class Lookup: public DefaultPermissions {};
1029821f1d3SAlan Somers class Open: public DefaultPermissions {};
103*ff4fbdf5SAlan Somers class Setattr: public DefaultPermissions {};
104*ff4fbdf5SAlan Somers class Unlink: public DefaultPermissions {};
1059821f1d3SAlan Somers 
106*ff4fbdf5SAlan Somers /*
107*ff4fbdf5SAlan Somers  * Test permission handling during create, mkdir, mknod, link, symlink, and
108*ff4fbdf5SAlan Somers  * rename vops (they all share a common path for permission checks in
109*ff4fbdf5SAlan Somers  * VOP_LOOKUP)
110*ff4fbdf5SAlan Somers  */
111*ff4fbdf5SAlan Somers class Create: public DefaultPermissions {
112*ff4fbdf5SAlan Somers public:
113*ff4fbdf5SAlan Somers 
114*ff4fbdf5SAlan Somers void expect_create(const char *relpath, uint64_t ino)
115*ff4fbdf5SAlan Somers {
116*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
117*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
118*ff4fbdf5SAlan Somers 			const char *name = (const char*)in->body.bytes +
119*ff4fbdf5SAlan Somers 				sizeof(fuse_open_in);
120*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_CREATE &&
121*ff4fbdf5SAlan Somers 				(0 == strcmp(relpath, name)));
122*ff4fbdf5SAlan Somers 		}, Eq(true)),
123*ff4fbdf5SAlan Somers 		_)
124*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
125*ff4fbdf5SAlan Somers 		SET_OUT_HEADER_LEN(out, create);
126*ff4fbdf5SAlan Somers 		out->body.create.entry.attr.mode = S_IFREG | 0644;
127*ff4fbdf5SAlan Somers 		out->body.create.entry.nodeid = ino;
128*ff4fbdf5SAlan Somers 		out->body.create.entry.entry_valid = UINT64_MAX;
129*ff4fbdf5SAlan Somers 		out->body.create.entry.attr_valid = UINT64_MAX;
130*ff4fbdf5SAlan Somers 	})));
131*ff4fbdf5SAlan Somers }
132*ff4fbdf5SAlan Somers 
133*ff4fbdf5SAlan Somers };
134*ff4fbdf5SAlan Somers 
135*ff4fbdf5SAlan Somers class Deleteextattr: public DefaultPermissions {
136*ff4fbdf5SAlan Somers public:
137*ff4fbdf5SAlan Somers void expect_removexattr()
138*ff4fbdf5SAlan Somers {
139*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
140*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
141*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_REMOVEXATTR);
142*ff4fbdf5SAlan Somers 		}, Eq(true)),
143*ff4fbdf5SAlan Somers 		_)
144*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(ReturnErrno(0)));
145*ff4fbdf5SAlan Somers }
146*ff4fbdf5SAlan Somers };
147*ff4fbdf5SAlan Somers 
148*ff4fbdf5SAlan Somers class Getextattr: public DefaultPermissions {
149*ff4fbdf5SAlan Somers public:
150*ff4fbdf5SAlan Somers void expect_getxattr(ProcessMockerT r)
151*ff4fbdf5SAlan Somers {
152*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
153*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
154*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_GETXATTR);
155*ff4fbdf5SAlan Somers 		}, Eq(true)),
156*ff4fbdf5SAlan Somers 		_)
157*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(r));
158*ff4fbdf5SAlan Somers }
159*ff4fbdf5SAlan Somers };
160*ff4fbdf5SAlan Somers 
161*ff4fbdf5SAlan Somers class Listextattr: public DefaultPermissions {
162*ff4fbdf5SAlan Somers public:
163*ff4fbdf5SAlan Somers void expect_listxattr()
164*ff4fbdf5SAlan Somers {
165*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
166*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
167*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_LISTXATTR);
168*ff4fbdf5SAlan Somers 		}, Eq(true)),
169*ff4fbdf5SAlan Somers 		_)
170*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) {
171*ff4fbdf5SAlan Somers 		out->body.listxattr.size = 0;
172*ff4fbdf5SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
173*ff4fbdf5SAlan Somers 	})));
174*ff4fbdf5SAlan Somers }
175*ff4fbdf5SAlan Somers };
176*ff4fbdf5SAlan Somers 
177*ff4fbdf5SAlan Somers class Rename: public DefaultPermissions {
178*ff4fbdf5SAlan Somers public:
179*ff4fbdf5SAlan Somers 	/*
180*ff4fbdf5SAlan Somers 	 * Expect a rename and respond with the given error.  Don't both to
181*ff4fbdf5SAlan Somers 	 * validate arguments; the tests in rename.cc do that.
182*ff4fbdf5SAlan Somers 	 */
183*ff4fbdf5SAlan Somers 	void expect_rename(int error)
184*ff4fbdf5SAlan Somers 	{
185*ff4fbdf5SAlan Somers 		EXPECT_CALL(*m_mock, process(
186*ff4fbdf5SAlan Somers 			ResultOf([=](auto in) {
187*ff4fbdf5SAlan Somers 				return (in->header.opcode == FUSE_RENAME);
188*ff4fbdf5SAlan Somers 			}, Eq(true)),
189*ff4fbdf5SAlan Somers 			_)
190*ff4fbdf5SAlan Somers 		).WillOnce(Invoke(ReturnErrno(error)));
191*ff4fbdf5SAlan Somers 	}
192*ff4fbdf5SAlan Somers };
193*ff4fbdf5SAlan Somers 
194*ff4fbdf5SAlan Somers class Setextattr: public DefaultPermissions {
195*ff4fbdf5SAlan Somers public:
196*ff4fbdf5SAlan Somers void expect_setxattr(int error)
197*ff4fbdf5SAlan Somers {
198*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
199*ff4fbdf5SAlan Somers 		ResultOf([=](auto in) {
200*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_SETXATTR);
201*ff4fbdf5SAlan Somers 		}, Eq(true)),
202*ff4fbdf5SAlan Somers 		_)
203*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(ReturnErrno(error)));
204*ff4fbdf5SAlan Somers }
205*ff4fbdf5SAlan Somers };
206*ff4fbdf5SAlan Somers 
207*ff4fbdf5SAlan Somers TEST_F(Access, eacces)
2089821f1d3SAlan Somers {
2099821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2109821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2119821f1d3SAlan Somers 	uint64_t ino = 42;
2129821f1d3SAlan Somers 	mode_t	access_mode = X_OK;
2139821f1d3SAlan Somers 
214*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
215*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
216*ff4fbdf5SAlan Somers 
217*ff4fbdf5SAlan Somers 	ASSERT_NE(0, access(FULLPATH, access_mode));
218*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
219*ff4fbdf5SAlan Somers }
220*ff4fbdf5SAlan Somers 
221*ff4fbdf5SAlan Somers TEST_F(Access, eacces_no_cached_attrs)
222*ff4fbdf5SAlan Somers {
223*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
224*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
225*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
226*ff4fbdf5SAlan Somers 	mode_t	access_mode = X_OK;
227*ff4fbdf5SAlan Somers 
228*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, 0, 1);
229*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0);
230*ff4fbdf5SAlan Somers 	expect_getattr(ino, S_IFREG | 0644, 0, 1);
2319821f1d3SAlan Somers 	/*
2329821f1d3SAlan Somers 	 * Once default_permissions is properly implemented, there might be
2339821f1d3SAlan Somers 	 * another FUSE_GETATTR or something in here.  But there should not be
2349821f1d3SAlan Somers 	 * a FUSE_ACCESS
2359821f1d3SAlan Somers 	 */
2369821f1d3SAlan Somers 
2379821f1d3SAlan Somers 	ASSERT_NE(0, access(FULLPATH, access_mode));
2389821f1d3SAlan Somers 	ASSERT_EQ(EACCES, errno);
2399821f1d3SAlan Somers }
2409821f1d3SAlan Somers 
241*ff4fbdf5SAlan Somers TEST_F(Access, ok)
2429821f1d3SAlan Somers {
2439821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2449821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2459821f1d3SAlan Somers 	uint64_t ino = 42;
2469821f1d3SAlan Somers 	mode_t	access_mode = R_OK;
2479821f1d3SAlan Somers 
248*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
249*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
2509821f1d3SAlan Somers 	/*
2519821f1d3SAlan Somers 	 * Once default_permissions is properly implemented, there might be
25291ff3a0dSAlan Somers 	 * another FUSE_GETATTR or something in here.
2539821f1d3SAlan Somers 	 */
2549821f1d3SAlan Somers 
2559821f1d3SAlan Somers 	ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
2569821f1d3SAlan Somers }
2579821f1d3SAlan Somers 
258*ff4fbdf5SAlan Somers TEST_F(Create, ok)
259*ff4fbdf5SAlan Somers {
260*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
261*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
262*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
263*ff4fbdf5SAlan Somers 	int fd;
264*ff4fbdf5SAlan Somers 
265*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1);
266*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
267*ff4fbdf5SAlan Somers 	expect_create(RELPATH, ino);
268*ff4fbdf5SAlan Somers 
269*ff4fbdf5SAlan Somers 	fd = open(FULLPATH, O_CREAT | O_EXCL, 0644);
270*ff4fbdf5SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
271*ff4fbdf5SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
272*ff4fbdf5SAlan Somers }
273*ff4fbdf5SAlan Somers 
274*ff4fbdf5SAlan Somers TEST_F(Create, eacces)
275*ff4fbdf5SAlan Somers {
276*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
277*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
278*ff4fbdf5SAlan Somers 
279*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
280*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
281*ff4fbdf5SAlan Somers 
282*ff4fbdf5SAlan Somers 	EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, 0644));
283*ff4fbdf5SAlan Somers 	EXPECT_EQ(EACCES, errno);
284*ff4fbdf5SAlan Somers }
285*ff4fbdf5SAlan Somers 
286*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, eacces)
287*ff4fbdf5SAlan Somers {
288*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
289*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
290*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
291*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
292*ff4fbdf5SAlan Somers 
293*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
294*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
295*ff4fbdf5SAlan Somers 
296*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
297*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
298*ff4fbdf5SAlan Somers }
299*ff4fbdf5SAlan Somers 
300*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, ok)
301*ff4fbdf5SAlan Somers {
302*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
303*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
304*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
305*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
306*ff4fbdf5SAlan Somers 
307*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
308*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
309*ff4fbdf5SAlan Somers 	expect_removexattr();
310*ff4fbdf5SAlan Somers 
311*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
312*ff4fbdf5SAlan Somers 		<< strerror(errno);
313*ff4fbdf5SAlan Somers }
314*ff4fbdf5SAlan Somers 
315*ff4fbdf5SAlan Somers /* Delete system attributes requires superuser privilege */
316*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, system)
317*ff4fbdf5SAlan Somers {
318*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
319*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
320*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
321*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
322*ff4fbdf5SAlan Somers 
323*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
324*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
325*ff4fbdf5SAlan Somers 
326*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
327*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
328*ff4fbdf5SAlan Somers }
329*ff4fbdf5SAlan Somers 
330*ff4fbdf5SAlan Somers /* Deleting user attributes merely requires WRITE privilege */
331*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, user)
332*ff4fbdf5SAlan Somers {
333*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
334*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
335*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
336*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
337*ff4fbdf5SAlan Somers 
338*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
339*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0);
340*ff4fbdf5SAlan Somers 	expect_removexattr();
341*ff4fbdf5SAlan Somers 
342*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
343*ff4fbdf5SAlan Somers 		<< strerror(errno);
344*ff4fbdf5SAlan Somers }
345*ff4fbdf5SAlan Somers 
346*ff4fbdf5SAlan Somers TEST_F(Getextattr, eacces)
347*ff4fbdf5SAlan Somers {
348*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
349*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
350*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
351*ff4fbdf5SAlan Somers 	char data[80];
352*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
353*ff4fbdf5SAlan Somers 
354*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
355*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0);
356*ff4fbdf5SAlan Somers 
357*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1,
358*ff4fbdf5SAlan Somers 		extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)));
359*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
360*ff4fbdf5SAlan Somers }
361*ff4fbdf5SAlan Somers 
362*ff4fbdf5SAlan Somers TEST_F(Getextattr, ok)
363*ff4fbdf5SAlan Somers {
364*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
365*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
366*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
367*ff4fbdf5SAlan Somers 	char data[80];
368*ff4fbdf5SAlan Somers 	const char value[] = "whatever";
369*ff4fbdf5SAlan Somers 	ssize_t value_len = strlen(value) + 1;
370*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
371*ff4fbdf5SAlan Somers 	ssize_t r;
372*ff4fbdf5SAlan Somers 
373*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
374*ff4fbdf5SAlan Somers 	/* Getting user attributes only requires read access */
375*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0444, UINT64_MAX, 0);
376*ff4fbdf5SAlan Somers 	expect_getxattr(
377*ff4fbdf5SAlan Somers 		ReturnImmediate([&](auto in __unused, auto out) {
378*ff4fbdf5SAlan Somers 			memcpy((void*)out->body.bytes, value, value_len);
379*ff4fbdf5SAlan Somers 			out->header.len = sizeof(out->header) + value_len;
380*ff4fbdf5SAlan Somers 		})
381*ff4fbdf5SAlan Somers 	);
382*ff4fbdf5SAlan Somers 
383*ff4fbdf5SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
384*ff4fbdf5SAlan Somers 	ASSERT_EQ(value_len, r)  << strerror(errno);
385*ff4fbdf5SAlan Somers 	EXPECT_STREQ(value, data);
386*ff4fbdf5SAlan Somers }
387*ff4fbdf5SAlan Somers 
388*ff4fbdf5SAlan Somers /* Getting system attributes requires superuser privileges */
389*ff4fbdf5SAlan Somers TEST_F(Getextattr, system)
390*ff4fbdf5SAlan Somers {
391*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
392*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
393*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
394*ff4fbdf5SAlan Somers 	char data[80];
395*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
396*ff4fbdf5SAlan Somers 
397*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
398*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
399*ff4fbdf5SAlan Somers 
400*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1,
401*ff4fbdf5SAlan Somers 		extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)));
402*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
403*ff4fbdf5SAlan Somers }
404*ff4fbdf5SAlan Somers 
405*ff4fbdf5SAlan Somers TEST_F(Listextattr, eacces)
406*ff4fbdf5SAlan Somers {
407*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
408*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
409*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
410*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
411*ff4fbdf5SAlan Somers 
412*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1);
413*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0);
414*ff4fbdf5SAlan Somers 
415*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
416*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
417*ff4fbdf5SAlan Somers }
418*ff4fbdf5SAlan Somers 
419*ff4fbdf5SAlan Somers TEST_F(Listextattr, ok)
420*ff4fbdf5SAlan Somers {
421*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
422*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
423*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
424*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
425*ff4fbdf5SAlan Somers 
426*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1);
427*ff4fbdf5SAlan Somers 	/* Listing user extended attributes merely requires read access */
428*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
429*ff4fbdf5SAlan Somers 	expect_listxattr();
430*ff4fbdf5SAlan Somers 
431*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0))
432*ff4fbdf5SAlan Somers 		<< strerror(errno);
433*ff4fbdf5SAlan Somers }
434*ff4fbdf5SAlan Somers 
435*ff4fbdf5SAlan Somers /* Listing system xattrs requires superuser privileges */
436*ff4fbdf5SAlan Somers TEST_F(Listextattr, system)
437*ff4fbdf5SAlan Somers {
438*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
439*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
440*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
441*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
442*ff4fbdf5SAlan Somers 
443*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1);
444*ff4fbdf5SAlan Somers 	/* Listing user extended attributes merely requires read access */
445*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
446*ff4fbdf5SAlan Somers 
447*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
448*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
449*ff4fbdf5SAlan Somers }
450*ff4fbdf5SAlan Somers 
451*ff4fbdf5SAlan Somers /* A component of the search path lacks execute permissions */
452*ff4fbdf5SAlan Somers TEST_F(Lookup, eacces)
453*ff4fbdf5SAlan Somers {
454*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_dir/some_file.txt";
455*ff4fbdf5SAlan Somers 	const char RELDIRPATH[] = "some_dir";
456*ff4fbdf5SAlan Somers 	//const char FINALPATH[] = "some_file.txt";
457*ff4fbdf5SAlan Somers 	uint64_t dir_ino = 42;
458*ff4fbdf5SAlan Somers 
459*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
460*ff4fbdf5SAlan Somers 	expect_lookup(RELDIRPATH, dir_ino, S_IFDIR | 0700, UINT64_MAX, 0);
461*ff4fbdf5SAlan Somers 
462*ff4fbdf5SAlan Somers 	EXPECT_EQ(-1, access(FULLPATH, F_OK));
463*ff4fbdf5SAlan Somers 	EXPECT_EQ(EACCES, errno);
464*ff4fbdf5SAlan Somers }
465*ff4fbdf5SAlan Somers 
466*ff4fbdf5SAlan Somers TEST_F(Open, eacces)
467*ff4fbdf5SAlan Somers {
468*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
469*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
470*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
471*ff4fbdf5SAlan Somers 
472*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
473*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
474*ff4fbdf5SAlan Somers 
475*ff4fbdf5SAlan Somers 	EXPECT_NE(0, open(FULLPATH, O_RDWR));
476*ff4fbdf5SAlan Somers 	EXPECT_EQ(EACCES, errno);
477*ff4fbdf5SAlan Somers }
478*ff4fbdf5SAlan Somers 
4799821f1d3SAlan Somers TEST_F(Open, ok)
4809821f1d3SAlan Somers {
4819821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4829821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4839821f1d3SAlan Somers 	uint64_t ino = 42;
4849821f1d3SAlan Somers 	int fd;
4859821f1d3SAlan Somers 
486*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
487*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX);
4889821f1d3SAlan Somers 	expect_open(ino, 0, 1);
4899821f1d3SAlan Somers 
4909821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
4919821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4929821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
4939821f1d3SAlan Somers }
4949821f1d3SAlan Somers 
495*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_srcdir)
496*ff4fbdf5SAlan Somers {
497*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/d/dst";
498*ff4fbdf5SAlan Somers 	const char RELDST[] = "d/dst";
499*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
500*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
501*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
502*ff4fbdf5SAlan Somers 
503*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, 0);
504*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
505*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(1, RELDST)
506*ff4fbdf5SAlan Somers 		.Times(AnyNumber())
507*ff4fbdf5SAlan Somers 		.WillRepeatedly(Invoke(ReturnErrno(ENOENT)));
508*ff4fbdf5SAlan Somers 
509*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
510*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
511*ff4fbdf5SAlan Somers }
512*ff4fbdf5SAlan Somers 
513*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_creating)
514*ff4fbdf5SAlan Somers {
515*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/d/dst";
516*ff4fbdf5SAlan Somers 	const char RELDSTDIR[] = "d";
517*ff4fbdf5SAlan Somers 	const char RELDST[] = "dst";
518*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
519*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
520*ff4fbdf5SAlan Somers 	uint64_t src_ino = 42;
521*ff4fbdf5SAlan Somers 	uint64_t dstdir_ino = 43;
522*ff4fbdf5SAlan Somers 
523*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
524*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
525*ff4fbdf5SAlan Somers 	expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX);
526*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
527*ff4fbdf5SAlan Somers 
528*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
529*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
530*ff4fbdf5SAlan Somers }
531*ff4fbdf5SAlan Somers 
532*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_removing)
533*ff4fbdf5SAlan Somers {
534*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/d/dst";
535*ff4fbdf5SAlan Somers 	const char RELDSTDIR[] = "d";
536*ff4fbdf5SAlan Somers 	const char RELDST[] = "dst";
537*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
538*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
539*ff4fbdf5SAlan Somers 	uint64_t src_ino = 42;
540*ff4fbdf5SAlan Somers 	uint64_t dstdir_ino = 43;
541*ff4fbdf5SAlan Somers 
542*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
543*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
544*ff4fbdf5SAlan Somers 	expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX);
545*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
546*ff4fbdf5SAlan Somers 
547*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
548*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
549*ff4fbdf5SAlan Somers }
550*ff4fbdf5SAlan Somers 
5519821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */
552*ff4fbdf5SAlan Somers TEST_F(Rename, DISABLED_eperm_on_sticky_srcdir)
553*ff4fbdf5SAlan Somers {
554*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/d/dst";
555*ff4fbdf5SAlan Somers 	const char RELDSTDIR[] = "d";
556*ff4fbdf5SAlan Somers 	const char RELDST[] = "dst";
557*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
558*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
559*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
560*ff4fbdf5SAlan Somers 	uint64_t dstdir_ino = 43;
561*ff4fbdf5SAlan Somers 
562*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0);
563*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
564*ff4fbdf5SAlan Somers 	expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0777, UINT64_MAX);
565*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
566*ff4fbdf5SAlan Somers 
567*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
568*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
569*ff4fbdf5SAlan Somers }
570*ff4fbdf5SAlan Somers 
571*ff4fbdf5SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */
572*ff4fbdf5SAlan Somers TEST_F(Rename, DISABLED_eperm_on_sticky_dstdir)
573*ff4fbdf5SAlan Somers {
574*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/d/dst";
575*ff4fbdf5SAlan Somers 	const char RELDSTDIR[] = "d";
576*ff4fbdf5SAlan Somers 	const char RELDST[] = "d/dst";
577*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
578*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
579*ff4fbdf5SAlan Somers 	uint64_t src_ino = 42;
580*ff4fbdf5SAlan Somers 	uint64_t dstdir_ino = 43;
581*ff4fbdf5SAlan Somers 	uint64_t dst_ino = 44;
582*ff4fbdf5SAlan Somers 
583*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
584*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX);
585*ff4fbdf5SAlan Somers 	expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 01777, UINT64_MAX);
586*ff4fbdf5SAlan Somers 	expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX, 0);
587*ff4fbdf5SAlan Somers 
588*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
589*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
590*ff4fbdf5SAlan Somers }
591*ff4fbdf5SAlan Somers 
592*ff4fbdf5SAlan Somers /* Successfully rename a file, overwriting the destination */
593*ff4fbdf5SAlan Somers TEST_F(Rename, ok)
594*ff4fbdf5SAlan Somers {
595*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/dst";
596*ff4fbdf5SAlan Somers 	const char RELDST[] = "dst";
597*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
598*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
599*ff4fbdf5SAlan Somers 	// The inode of the already-existing destination file
600*ff4fbdf5SAlan Somers 	uint64_t dst_ino = 2;
601*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
602*ff4fbdf5SAlan Somers 
603*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, geteuid());
604*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX);
605*ff4fbdf5SAlan Somers 	expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX);
606*ff4fbdf5SAlan Somers 	expect_rename(0);
607*ff4fbdf5SAlan Somers 
608*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
609*ff4fbdf5SAlan Somers }
610*ff4fbdf5SAlan Somers 
611*ff4fbdf5SAlan Somers TEST_F(Rename, ok_to_remove_src_because_of_stickiness)
612*ff4fbdf5SAlan Somers {
613*ff4fbdf5SAlan Somers 	const char FULLDST[] = "mountpoint/dst";
614*ff4fbdf5SAlan Somers 	const char RELDST[] = "dst";
615*ff4fbdf5SAlan Somers 	const char FULLSRC[] = "mountpoint/src";
616*ff4fbdf5SAlan Somers 	const char RELSRC[] = "src";
617*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
618*ff4fbdf5SAlan Somers 
619*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0);
620*ff4fbdf5SAlan Somers 	expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
621*ff4fbdf5SAlan Somers 	EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
622*ff4fbdf5SAlan Somers 	expect_rename(0);
623*ff4fbdf5SAlan Somers 
624*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
625*ff4fbdf5SAlan Somers }
626*ff4fbdf5SAlan Somers 
627*ff4fbdf5SAlan Somers TEST_F(Setattr, ok)
628*ff4fbdf5SAlan Somers {
629*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
630*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
631*ff4fbdf5SAlan Somers 	const uint64_t ino = 42;
632*ff4fbdf5SAlan Somers 	const mode_t oldmode = 0755;
633*ff4fbdf5SAlan Somers 	const mode_t newmode = 0644;
634*ff4fbdf5SAlan Somers 
635*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
636*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid());
637*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
638*ff4fbdf5SAlan Somers 		ResultOf([](auto in) {
639*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_SETATTR &&
640*ff4fbdf5SAlan Somers 				in->header.nodeid == ino &&
641*ff4fbdf5SAlan Somers 				in->body.setattr.mode == newmode);
642*ff4fbdf5SAlan Somers 		}, Eq(true)),
643*ff4fbdf5SAlan Somers 		_)
644*ff4fbdf5SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) {
645*ff4fbdf5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
646*ff4fbdf5SAlan Somers 		out->body.attr.attr.mode = S_IFREG | newmode;
647*ff4fbdf5SAlan Somers 	})));
648*ff4fbdf5SAlan Somers 
649*ff4fbdf5SAlan Somers 	EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
650*ff4fbdf5SAlan Somers }
651*ff4fbdf5SAlan Somers 
652*ff4fbdf5SAlan Somers TEST_F(Setattr, eacces)
653*ff4fbdf5SAlan Somers {
654*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
655*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
656*ff4fbdf5SAlan Somers 	const uint64_t ino = 42;
657*ff4fbdf5SAlan Somers 	const mode_t oldmode = 0755;
658*ff4fbdf5SAlan Somers 	const mode_t newmode = 0644;
659*ff4fbdf5SAlan Somers 
660*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
661*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, 0);
662*ff4fbdf5SAlan Somers 	EXPECT_CALL(*m_mock, process(
663*ff4fbdf5SAlan Somers 		ResultOf([](auto in) {
664*ff4fbdf5SAlan Somers 			return (in->header.opcode == FUSE_SETATTR);
665*ff4fbdf5SAlan Somers 		}, Eq(true)),
666*ff4fbdf5SAlan Somers 		_)
667*ff4fbdf5SAlan Somers 	).Times(0);
668*ff4fbdf5SAlan Somers 
669*ff4fbdf5SAlan Somers 	EXPECT_NE(0, chmod(FULLPATH, newmode));
670*ff4fbdf5SAlan Somers 	EXPECT_EQ(EPERM, errno);
671*ff4fbdf5SAlan Somers }
672*ff4fbdf5SAlan Somers 
673*ff4fbdf5SAlan Somers TEST_F(Setextattr, ok)
674*ff4fbdf5SAlan Somers {
675*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
676*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
677*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
678*ff4fbdf5SAlan Somers 	const char value[] = "whatever";
679*ff4fbdf5SAlan Somers 	ssize_t value_len = strlen(value) + 1;
680*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
681*ff4fbdf5SAlan Somers 	ssize_t r;
682*ff4fbdf5SAlan Somers 
683*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
684*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
685*ff4fbdf5SAlan Somers 	expect_setxattr(0);
686*ff4fbdf5SAlan Somers 
687*ff4fbdf5SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len);
688*ff4fbdf5SAlan Somers 	ASSERT_EQ(value_len, r) << strerror(errno);
689*ff4fbdf5SAlan Somers }
690*ff4fbdf5SAlan Somers 
691*ff4fbdf5SAlan Somers TEST_F(Setextattr, eacces)
692*ff4fbdf5SAlan Somers {
693*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
694*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
695*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
696*ff4fbdf5SAlan Somers 	const char value[] = "whatever";
697*ff4fbdf5SAlan Somers 	ssize_t value_len = strlen(value) + 1;
698*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
699*ff4fbdf5SAlan Somers 
700*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
701*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
702*ff4fbdf5SAlan Somers 
703*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1,
704*ff4fbdf5SAlan Somers 		extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len));
705*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
706*ff4fbdf5SAlan Somers }
707*ff4fbdf5SAlan Somers 
708*ff4fbdf5SAlan Somers // Setting system attributes requires superuser privileges
709*ff4fbdf5SAlan Somers TEST_F(Setextattr, system)
710*ff4fbdf5SAlan Somers {
711*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
712*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
713*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
714*ff4fbdf5SAlan Somers 	const char value[] = "whatever";
715*ff4fbdf5SAlan Somers 	ssize_t value_len = strlen(value) + 1;
716*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
717*ff4fbdf5SAlan Somers 
718*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
719*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid());
720*ff4fbdf5SAlan Somers 
721*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1,
722*ff4fbdf5SAlan Somers 		extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len));
723*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
724*ff4fbdf5SAlan Somers }
725*ff4fbdf5SAlan Somers 
726*ff4fbdf5SAlan Somers // Setting user attributes merely requires write privileges
727*ff4fbdf5SAlan Somers TEST_F(Setextattr, user)
728*ff4fbdf5SAlan Somers {
729*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
730*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
731*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
732*ff4fbdf5SAlan Somers 	const char value[] = "whatever";
733*ff4fbdf5SAlan Somers 	ssize_t value_len = strlen(value) + 1;
734*ff4fbdf5SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
735*ff4fbdf5SAlan Somers 	ssize_t r;
736*ff4fbdf5SAlan Somers 
737*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
738*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0);
739*ff4fbdf5SAlan Somers 	expect_setxattr(0);
740*ff4fbdf5SAlan Somers 
741*ff4fbdf5SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len);
742*ff4fbdf5SAlan Somers 	ASSERT_EQ(value_len, r) << strerror(errno);
743*ff4fbdf5SAlan Somers }
744*ff4fbdf5SAlan Somers 
745*ff4fbdf5SAlan Somers TEST_F(Unlink, ok)
7469821f1d3SAlan Somers {
7479821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7489821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7499821f1d3SAlan Somers 	uint64_t ino = 42;
7509821f1d3SAlan Somers 
751*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1);
752*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
753*ff4fbdf5SAlan Somers 	expect_unlink(1, RELPATH, 0);
7549821f1d3SAlan Somers 
755*ff4fbdf5SAlan Somers 	ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
756*ff4fbdf5SAlan Somers }
757*ff4fbdf5SAlan Somers 
758*ff4fbdf5SAlan Somers TEST_F(Unlink, unwritable_directory)
759*ff4fbdf5SAlan Somers {
760*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
761*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
762*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
763*ff4fbdf5SAlan Somers 
764*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1);
765*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid());
766*ff4fbdf5SAlan Somers 
767*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, unlink(FULLPATH));
768*ff4fbdf5SAlan Somers 	ASSERT_EQ(EACCES, errno);
769*ff4fbdf5SAlan Somers }
770*ff4fbdf5SAlan Somers 
771*ff4fbdf5SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */
772*ff4fbdf5SAlan Somers TEST_F(Unlink, DISABLED_sticky_directory)
773*ff4fbdf5SAlan Somers {
774*ff4fbdf5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
775*ff4fbdf5SAlan Somers 	const char RELPATH[] = "some_file.txt";
776*ff4fbdf5SAlan Somers 	uint64_t ino = 42;
777*ff4fbdf5SAlan Somers 
778*ff4fbdf5SAlan Somers 	expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1);
779*ff4fbdf5SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0);
780*ff4fbdf5SAlan Somers 
781*ff4fbdf5SAlan Somers 	ASSERT_EQ(-1, unlink(FULLPATH));
782*ff4fbdf5SAlan Somers 	ASSERT_EQ(EPERM, errno);
7839821f1d3SAlan Somers }
784