xref: /freebsd/tests/sys/fs/fusefs/xattr.cc (revision 088cc7d221bb0743fc5ec12de983559b812366bd)
19821f1d3SAlan Somers /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 
31d53a96f1SAlan Somers /* Tests for all things relating to extended attributes and FUSE */
32d53a96f1SAlan Somers 
339821f1d3SAlan Somers extern "C" {
349821f1d3SAlan Somers #include <sys/types.h>
359821f1d3SAlan Somers #include <sys/extattr.h>
365e633330SAlan Somers #include <sys/wait.h>
375e633330SAlan Somers #include <semaphore.h>
385e633330SAlan Somers #include <signal.h>
399821f1d3SAlan Somers #include <string.h>
409821f1d3SAlan Somers }
419821f1d3SAlan Somers 
429821f1d3SAlan Somers #include "mockfs.hh"
439821f1d3SAlan Somers #include "utils.hh"
449821f1d3SAlan Somers 
459821f1d3SAlan Somers using namespace testing;
469821f1d3SAlan Somers 
479821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
489821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
495e633330SAlan Somers static sem_t killer_semaphore;
505e633330SAlan Somers 
killer(void * target)515e633330SAlan Somers void* killer(void* target) {
525e633330SAlan Somers 	pid_t pid = *(pid_t*)target;
535e633330SAlan Somers 	sem_wait(&killer_semaphore);
545e633330SAlan Somers 	if (verbosity > 1)
555e633330SAlan Somers 		printf("Killing! pid %d\n", pid);
565e633330SAlan Somers 	kill(pid, SIGINT);
575e633330SAlan Somers 
585e633330SAlan Somers 	return(NULL);
595e633330SAlan Somers }
609821f1d3SAlan Somers 
619821f1d3SAlan Somers class Xattr: public FuseTest {
629821f1d3SAlan Somers public:
expect_listxattr(uint64_t ino,uint32_t size,ProcessMockerT r,Sequence * seq=NULL)635e633330SAlan Somers void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
645e633330SAlan Somers     Sequence *seq = NULL)
659821f1d3SAlan Somers {
665e633330SAlan Somers 	if (seq == NULL) {
679821f1d3SAlan Somers 		EXPECT_CALL(*m_mock, process(
689821f1d3SAlan Somers 			ResultOf([=](auto in) {
6929edc611SAlan Somers 				return (in.header.opcode == FUSE_LISTXATTR &&
7029edc611SAlan Somers 					in.header.nodeid == ino &&
7129edc611SAlan Somers 					in.body.listxattr.size == size);
729821f1d3SAlan Somers 			}, Eq(true)),
739821f1d3SAlan Somers 			_)
749821f1d3SAlan Somers 		).WillOnce(Invoke(r))
759821f1d3SAlan Somers 		.RetiresOnSaturation();
765e633330SAlan Somers 	} else {
775e633330SAlan Somers 		EXPECT_CALL(*m_mock, process(
785e633330SAlan Somers 			ResultOf([=](auto in) {
795e633330SAlan Somers 				return (in.header.opcode == FUSE_LISTXATTR &&
805e633330SAlan Somers 					in.header.nodeid == ino &&
815e633330SAlan Somers 					in.body.listxattr.size == size);
825e633330SAlan Somers 			}, Eq(true)),
835e633330SAlan Somers 			_)
845e633330SAlan Somers 		).InSequence(*seq)
855e633330SAlan Somers 		.WillOnce(Invoke(r))
865e633330SAlan Somers 		.RetiresOnSaturation();
875e633330SAlan Somers 	}
889821f1d3SAlan Somers }
899821f1d3SAlan Somers 
expect_removexattr(uint64_t ino,const char * attr,int error)909821f1d3SAlan Somers void expect_removexattr(uint64_t ino, const char *attr, int error)
919821f1d3SAlan Somers {
929821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
939821f1d3SAlan Somers 		ResultOf([=](auto in) {
9429edc611SAlan Somers 			const char *a = (const char*)in.body.bytes;
9529edc611SAlan Somers 			return (in.header.opcode == FUSE_REMOVEXATTR &&
9629edc611SAlan Somers 				in.header.nodeid == ino &&
979821f1d3SAlan Somers 				0 == strcmp(attr, a));
989821f1d3SAlan Somers 		}, Eq(true)),
999821f1d3SAlan Somers 		_)
1009821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnErrno(error)));
1019821f1d3SAlan Somers }
1029821f1d3SAlan Somers 
expect_setxattr(uint64_t ino,const char * attr,const char * value,ProcessMockerT r)1039821f1d3SAlan Somers void expect_setxattr(uint64_t ino, const char *attr, const char *value,
1049821f1d3SAlan Somers 	ProcessMockerT r)
1059821f1d3SAlan Somers {
1069821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1079821f1d3SAlan Somers 		ResultOf([=](auto in) {
10829edc611SAlan Somers 			const char *a = (const char*)in.body.bytes +
1099821f1d3SAlan Somers 				sizeof(fuse_setxattr_in);
1109821f1d3SAlan Somers 			const char *v = a + strlen(a) + 1;
11129edc611SAlan Somers 			return (in.header.opcode == FUSE_SETXATTR &&
11229edc611SAlan Somers 				in.header.nodeid == ino &&
1139821f1d3SAlan Somers 				0 == strcmp(attr, a) &&
1149821f1d3SAlan Somers 				0 == strcmp(value, v));
1159821f1d3SAlan Somers 		}, Eq(true)),
1169821f1d3SAlan Somers 		_)
1179821f1d3SAlan Somers 	).WillOnce(Invoke(r));
1189821f1d3SAlan Somers }
1199821f1d3SAlan Somers 
1209821f1d3SAlan Somers };
1219821f1d3SAlan Somers 
1229821f1d3SAlan Somers class Getxattr: public Xattr {};
1235e633330SAlan Somers 
1249821f1d3SAlan Somers class Listxattr: public Xattr {};
1255e633330SAlan Somers 
1265e633330SAlan Somers /* Listxattr tests that need to use a signal */
1275e633330SAlan Somers class ListxattrSig: public Listxattr {
1285e633330SAlan Somers public:
1295e633330SAlan Somers pthread_t m_killer_th;
1305e633330SAlan Somers pid_t m_child;
1315e633330SAlan Somers 
SetUp()1325e633330SAlan Somers void SetUp() {
1335e633330SAlan Somers 	/*
1345e633330SAlan Somers 	 * Mount with -o nointr so the mount can't get interrupted while
1355e633330SAlan Somers 	 * waiting for a response from the server
1365e633330SAlan Somers 	 */
1375e633330SAlan Somers 	m_nointr = true;
1385e633330SAlan Somers 	FuseTest::SetUp();
1395e633330SAlan Somers 
1405e633330SAlan Somers 	ASSERT_EQ(0, sem_init(&killer_semaphore, 0, 0)) << strerror(errno);
1415e633330SAlan Somers }
1425e633330SAlan Somers 
TearDown()1435e633330SAlan Somers void TearDown() {
1445e633330SAlan Somers 	if (m_killer_th != NULL) {
1455e633330SAlan Somers 		pthread_join(m_killer_th, NULL);
1465e633330SAlan Somers 	}
1475e633330SAlan Somers 
1485e633330SAlan Somers 	sem_destroy(&killer_semaphore);
1495e633330SAlan Somers 
1505e633330SAlan Somers 	FuseTest::TearDown();
1515e633330SAlan Somers }
1525e633330SAlan Somers };
1535e633330SAlan Somers 
1549821f1d3SAlan Somers class Removexattr: public Xattr {};
1559821f1d3SAlan Somers class Setxattr: public Xattr {};
156666f8543SAlan Somers class RofsXattr: public Xattr {
157666f8543SAlan Somers public:
SetUp()158666f8543SAlan Somers virtual void SetUp() {
159666f8543SAlan Somers 	m_ro = true;
160666f8543SAlan Somers 	Xattr::SetUp();
161666f8543SAlan Somers }
162666f8543SAlan Somers };
1639821f1d3SAlan Somers 
1649821f1d3SAlan Somers /*
1659821f1d3SAlan Somers  * If the extended attribute does not exist on this file, the daemon should
1669821f1d3SAlan Somers  * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the
1679821f1d3SAlan Somers  * correct errror code)
1689821f1d3SAlan Somers  */
TEST_F(Getxattr,enoattr)1699821f1d3SAlan Somers TEST_F(Getxattr, enoattr)
1709821f1d3SAlan Somers {
1719821f1d3SAlan Somers 	char data[80];
1729821f1d3SAlan Somers 	uint64_t ino = 42;
1739821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
1749821f1d3SAlan Somers 	ssize_t r;
1759821f1d3SAlan Somers 
1769821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
1779821f1d3SAlan Somers 	expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
1789821f1d3SAlan Somers 
1799821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
1809821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
1819821f1d3SAlan Somers 	ASSERT_EQ(ENOATTR, errno);
1829821f1d3SAlan Somers }
1839821f1d3SAlan Somers 
1849821f1d3SAlan Somers /*
1859821f1d3SAlan Somers  * If the filesystem returns ENOSYS, then it will be treated as a permanent
1869821f1d3SAlan Somers  * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP
1879821f1d3SAlan Somers  * without querying the filesystem daemon
1889821f1d3SAlan Somers  */
TEST_F(Getxattr,enosys)1891f4a83f9SAlan Somers TEST_F(Getxattr, enosys)
1909821f1d3SAlan Somers {
1919821f1d3SAlan Somers 	char data[80];
1929821f1d3SAlan Somers 	uint64_t ino = 42;
1939821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
1949821f1d3SAlan Somers 	ssize_t r;
1959821f1d3SAlan Somers 
1961f4a83f9SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
1979821f1d3SAlan Somers 	expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS));
1989821f1d3SAlan Somers 
1999821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
2009821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
2019821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
2029821f1d3SAlan Somers 
2039821f1d3SAlan Somers 	/* Subsequent attempts should not query the filesystem at all */
2049821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
2059821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
2069821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
2079821f1d3SAlan Somers }
2089821f1d3SAlan Somers 
2099821f1d3SAlan Somers /*
2109821f1d3SAlan Somers  * On FreeBSD, if the user passes an insufficiently large buffer then the
2119821f1d3SAlan Somers  * filesystem is supposed to copy as much of the attribute's value as will fit.
2129821f1d3SAlan Somers  *
2139821f1d3SAlan Somers  * On Linux, however, the filesystem is supposed to return ERANGE.
2149821f1d3SAlan Somers  *
2159821f1d3SAlan Somers  * libfuse specifies the Linux behavior.  However, that's probably an error.
2169821f1d3SAlan Somers  * It would probably be correct for the filesystem to use platform-dependent
2179821f1d3SAlan Somers  * behavior.
2189821f1d3SAlan Somers  *
2199821f1d3SAlan Somers  * This test case covers a filesystem that uses the Linux behavior
2205e633330SAlan Somers  * TODO: require FreeBSD Behavior.
2219821f1d3SAlan Somers  */
TEST_F(Getxattr,erange)2229821f1d3SAlan Somers TEST_F(Getxattr, erange)
2239821f1d3SAlan Somers {
2249821f1d3SAlan Somers 	char data[10];
2259821f1d3SAlan Somers 	uint64_t ino = 42;
2269821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
2279821f1d3SAlan Somers 	ssize_t r;
2289821f1d3SAlan Somers 
2299821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
2309821f1d3SAlan Somers 	expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE));
2319821f1d3SAlan Somers 
2329821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
2339821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
2349821f1d3SAlan Somers 	ASSERT_EQ(ERANGE, errno);
2359821f1d3SAlan Somers }
2369821f1d3SAlan Somers 
2379821f1d3SAlan Somers /*
2389821f1d3SAlan Somers  * If the user passes a 0-length buffer, then the daemon should just return the
2399821f1d3SAlan Somers  * size of the attribute
2409821f1d3SAlan Somers  */
TEST_F(Getxattr,size_only)2419821f1d3SAlan Somers TEST_F(Getxattr, size_only)
2429821f1d3SAlan Somers {
2439821f1d3SAlan Somers 	uint64_t ino = 42;
2449821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
2459821f1d3SAlan Somers 
2469821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
2479821f1d3SAlan Somers 	expect_getxattr(ino, "user.foo",
24829edc611SAlan Somers 		ReturnImmediate([](auto in __unused, auto& out) {
2499821f1d3SAlan Somers 			SET_OUT_HEADER_LEN(out, getxattr);
25029edc611SAlan Somers 			out.body.getxattr.size = 99;
2519821f1d3SAlan Somers 		})
2529821f1d3SAlan Somers 	);
2539821f1d3SAlan Somers 
2549821f1d3SAlan Somers 	ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0))
2559821f1d3SAlan Somers 		<< strerror(errno);;
2569821f1d3SAlan Somers }
2579821f1d3SAlan Somers 
2589821f1d3SAlan Somers /*
2599821f1d3SAlan Somers  * Successfully get an attribute from the system namespace
2609821f1d3SAlan Somers  */
TEST_F(Getxattr,system)2619821f1d3SAlan Somers TEST_F(Getxattr, system)
2629821f1d3SAlan Somers {
2639821f1d3SAlan Somers 	uint64_t ino = 42;
2649821f1d3SAlan Somers 	char data[80];
2659821f1d3SAlan Somers 	const char value[] = "whatever";
2669821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
2679821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
2689821f1d3SAlan Somers 	ssize_t r;
2699821f1d3SAlan Somers 
2709821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
2719821f1d3SAlan Somers 	expect_getxattr(ino, "system.foo",
27229edc611SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
27329edc611SAlan Somers 			memcpy((void*)out.body.bytes, value, value_len);
27429edc611SAlan Somers 			out.header.len = sizeof(out.header) + value_len;
2759821f1d3SAlan Somers 		})
2769821f1d3SAlan Somers 	);
2779821f1d3SAlan Somers 
2789821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
2799821f1d3SAlan Somers 	ASSERT_EQ(value_len, r)  << strerror(errno);
2809821f1d3SAlan Somers 	EXPECT_STREQ(value, data);
2819821f1d3SAlan Somers }
2829821f1d3SAlan Somers 
2839821f1d3SAlan Somers /*
2849821f1d3SAlan Somers  * Successfully get an attribute from the user namespace
2859821f1d3SAlan Somers  */
TEST_F(Getxattr,user)2869821f1d3SAlan Somers TEST_F(Getxattr, user)
2879821f1d3SAlan Somers {
2889821f1d3SAlan Somers 	uint64_t ino = 42;
2899821f1d3SAlan Somers 	char data[80];
2909821f1d3SAlan Somers 	const char value[] = "whatever";
2919821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
2929821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
2939821f1d3SAlan Somers 	ssize_t r;
2949821f1d3SAlan Somers 
2959821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
2969821f1d3SAlan Somers 	expect_getxattr(ino, "user.foo",
29729edc611SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
29829edc611SAlan Somers 			memcpy((void*)out.body.bytes, value, value_len);
29929edc611SAlan Somers 			out.header.len = sizeof(out.header) + value_len;
3009821f1d3SAlan Somers 		})
3019821f1d3SAlan Somers 	);
3029821f1d3SAlan Somers 
3039821f1d3SAlan Somers 	r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
3049821f1d3SAlan Somers 	ASSERT_EQ(value_len, r)  << strerror(errno);
3059821f1d3SAlan Somers 	EXPECT_STREQ(value, data);
3069821f1d3SAlan Somers }
3079821f1d3SAlan Somers 
3089821f1d3SAlan Somers /*
3099821f1d3SAlan Somers  * If the filesystem returns ENOSYS, then it will be treated as a permanent
3109821f1d3SAlan Somers  * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP
3119821f1d3SAlan Somers  * without querying the filesystem daemon
3129821f1d3SAlan Somers  */
TEST_F(Listxattr,enosys)3131f4a83f9SAlan Somers TEST_F(Listxattr, enosys)
3149821f1d3SAlan Somers {
3159821f1d3SAlan Somers 	uint64_t ino = 42;
3169821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
3179821f1d3SAlan Somers 
3181f4a83f9SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
3199821f1d3SAlan Somers 	expect_listxattr(ino, 0, ReturnErrno(ENOSYS));
3209821f1d3SAlan Somers 
3219821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
3229821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
3239821f1d3SAlan Somers 
3249821f1d3SAlan Somers 	/* Subsequent attempts should not query the filesystem at all */
3259821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
3269821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
3279821f1d3SAlan Somers }
3289821f1d3SAlan Somers 
3299821f1d3SAlan Somers /*
3309821f1d3SAlan Somers  * Listing extended attributes failed because they aren't configured on this
3319821f1d3SAlan Somers  * filesystem
3329821f1d3SAlan Somers  */
TEST_F(Listxattr,enotsup)3339821f1d3SAlan Somers TEST_F(Listxattr, enotsup)
3349821f1d3SAlan Somers {
3359821f1d3SAlan Somers 	uint64_t ino = 42;
3369821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
3379821f1d3SAlan Somers 
3389821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
3399821f1d3SAlan Somers 	expect_listxattr(ino, 0, ReturnErrno(ENOTSUP));
3409821f1d3SAlan Somers 
3419821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
3429821f1d3SAlan Somers 	ASSERT_EQ(ENOTSUP, errno);
3439821f1d3SAlan Somers }
3449821f1d3SAlan Somers 
3459821f1d3SAlan Somers /*
3465e633330SAlan Somers  * On FreeBSD, if the user passes an insufficiently large buffer to
3475e633330SAlan Somers  * extattr_list_file(2) or VOP_LISTEXTATTR(9), then the file system is supposed
3485e633330SAlan Somers  * to copy as much of the attribute's value as will fit.
3499821f1d3SAlan Somers  *
3505e633330SAlan Somers  * On Linux, however, the file system is supposed to return ERANGE if an
3515e633330SAlan Somers  * insufficiently large buffer is passed to listxattr(2).
3529821f1d3SAlan Somers  *
353*088cc7d2SAlexander Ziaee  * fusefs(4) must guarantee the usual FreeBSD behavior.
3549821f1d3SAlan Somers  */
TEST_F(Listxattr,erange)3559821f1d3SAlan Somers TEST_F(Listxattr, erange)
3569821f1d3SAlan Somers {
3579821f1d3SAlan Somers 	uint64_t ino = 42;
3589821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
3595e633330SAlan Somers 	char attrs[9] = "user.foo";
3605e633330SAlan Somers 	char expected[3] = {3, 'f', 'o'};
3615e633330SAlan Somers 	char buf[3];
3629821f1d3SAlan Somers 
3639821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
3645e633330SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
3655e633330SAlan Somers 	{
3665e633330SAlan Somers 		out.body.listxattr.size = sizeof(attrs);
3675e633330SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
3685e633330SAlan Somers 	}));
3695e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs),
3705e633330SAlan Somers 	ReturnImmediate([&](auto in __unused, auto& out) {
3715e633330SAlan Somers 		memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
3725e633330SAlan Somers 		out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
3735e633330SAlan Somers 	}));
3749821f1d3SAlan Somers 
3755e633330SAlan Somers 
3765e633330SAlan Somers 	ASSERT_EQ(static_cast<ssize_t>(sizeof(buf)),
3775e633330SAlan Somers 		  extattr_list_file(FULLPATH, ns, buf, sizeof(buf)));
3785e633330SAlan Somers 	ASSERT_EQ(0, memcmp(expected, buf, sizeof(buf)));
3795e633330SAlan Somers }
3805e633330SAlan Somers 
3815e633330SAlan Somers /*
3825e633330SAlan Somers  * A buggy or malicious file system always returns ERANGE, even if we pass an
3835e633330SAlan Somers  * appropriately sized buffer.  That will send the kernel into an infinite
3845e633330SAlan Somers  * loop.  This test will ensure that the loop is interruptible by killing the
3855e633330SAlan Somers  * blocked process with SIGINT.
3865e633330SAlan Somers  */
TEST_F(ListxattrSig,erange_forever)3875e633330SAlan Somers TEST_F(ListxattrSig, erange_forever)
3885e633330SAlan Somers {
3895e633330SAlan Somers 	uint64_t ino = 42;
3905e633330SAlan Somers 	uint32_t lie_size = 10;
3915e633330SAlan Somers 	int status;
3925e633330SAlan Somers 
3935e633330SAlan Somers 	fork(false, &status, [&] {
3945e633330SAlan Somers 		EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
3955e633330SAlan Somers 		.WillRepeatedly(Invoke(
3965e633330SAlan Somers 			ReturnImmediate([=](auto in __unused, auto& out) {
3975e633330SAlan Somers 			SET_OUT_HEADER_LEN(out, entry);
3985e633330SAlan Somers 			out.body.entry.attr.mode = S_IFREG | 0644;
3995e633330SAlan Somers 			out.body.entry.nodeid = ino;
4005e633330SAlan Somers 			out.body.entry.attr.nlink = 1;
4015e633330SAlan Somers 			out.body.entry.attr_valid = UINT64_MAX;
4025e633330SAlan Somers 			out.body.entry.entry_valid = UINT64_MAX;
4035e633330SAlan Somers 		})));
4045e633330SAlan Somers 		EXPECT_CALL(*m_mock, process(
4055e633330SAlan Somers 			ResultOf([=](auto in) {
4065e633330SAlan Somers 				return (in.header.opcode == FUSE_LISTXATTR &&
4075e633330SAlan Somers 					in.header.nodeid == ino &&
4085e633330SAlan Somers 					in.body.listxattr.size == 0);
4095e633330SAlan Somers 			}, Eq(true)),
4105e633330SAlan Somers 			_)
4115e633330SAlan Somers 		).WillRepeatedly(ReturnImmediate([=](auto i __unused, auto& out)
4125e633330SAlan Somers 		{
4135e633330SAlan Somers 			/* The file system requests 10 bytes, but it's a lie */
4145e633330SAlan Somers 			out.body.listxattr.size = lie_size;
4155e633330SAlan Somers 			SET_OUT_HEADER_LEN(out, listxattr);
4165e633330SAlan Somers 			/*
4175e633330SAlan Somers 			 * We can send the signal any time after fusefs enters
4185e633330SAlan Somers 			 * VOP_LISTEXTATTR
4195e633330SAlan Somers 			 */
4205e633330SAlan Somers 			sem_post(&killer_semaphore);
4215e633330SAlan Somers 		}));
4225e633330SAlan Somers 		/*
4235e633330SAlan Somers 		 * Even though the kernel faithfully respects our size request,
4245e633330SAlan Somers 		 * we'll return ERANGE anyway.
4255e633330SAlan Somers 		 */
4265e633330SAlan Somers 		EXPECT_CALL(*m_mock, process(
4275e633330SAlan Somers 			ResultOf([=](auto in) {
4285e633330SAlan Somers 				return (in.header.opcode == FUSE_LISTXATTR &&
4295e633330SAlan Somers 					in.header.nodeid == ino &&
4305e633330SAlan Somers 					in.body.listxattr.size == lie_size);
4315e633330SAlan Somers 			}, Eq(true)),
4325e633330SAlan Somers 			_)
4335e633330SAlan Somers 		).WillRepeatedly(ReturnErrno(ERANGE));
4345e633330SAlan Somers 
4355e633330SAlan Somers 		ASSERT_EQ(0, pthread_create(&m_killer_th, NULL, killer,
4365e633330SAlan Somers 					    &m_mock->m_child_pid))
4375e633330SAlan Somers 			<< strerror(errno);
4385e633330SAlan Somers 
4395e633330SAlan Somers 	}, [] {
4405e633330SAlan Somers 		/* Child process will block until it gets signaled */
4415e633330SAlan Somers 		int ns = EXTATTR_NAMESPACE_USER;
4425e633330SAlan Somers 		char buf[3];
4435e633330SAlan Somers 		extattr_list_file(FULLPATH, ns, buf, sizeof(buf));
4445e633330SAlan Somers 		return 0;
4455e633330SAlan Somers 	}
4465e633330SAlan Somers 	);
4475e633330SAlan Somers 
4485e633330SAlan Somers 	ASSERT_TRUE(WIFSIGNALED(status));
4499821f1d3SAlan Somers }
4509821f1d3SAlan Somers 
4519821f1d3SAlan Somers /*
4529821f1d3SAlan Somers  * Get the size of the list that it would take to list no extended attributes
4539821f1d3SAlan Somers  */
TEST_F(Listxattr,size_only_empty)4549821f1d3SAlan Somers TEST_F(Listxattr, size_only_empty)
4559821f1d3SAlan Somers {
4569821f1d3SAlan Somers 	uint64_t ino = 42;
4579821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
4589821f1d3SAlan Somers 
4599821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
46029edc611SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
46129edc611SAlan Somers 		out.body.listxattr.size = 0;
4629821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
4639821f1d3SAlan Somers 	}));
4649821f1d3SAlan Somers 
4659821f1d3SAlan Somers 	ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0))
4669821f1d3SAlan Somers 		<< strerror(errno);
4679821f1d3SAlan Somers }
4689821f1d3SAlan Somers 
4699821f1d3SAlan Somers /*
4709821f1d3SAlan Somers  * Get the size of the list that it would take to list some extended
4719821f1d3SAlan Somers  * attributes.  Due to the format differences between a FreeBSD and a
4729821f1d3SAlan Somers  * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer
4739821f1d3SAlan Somers  * and get the whole list, then convert it, just to figure out its size.
4749821f1d3SAlan Somers  */
TEST_F(Listxattr,size_only_nonempty)4759821f1d3SAlan Somers TEST_F(Listxattr, size_only_nonempty)
4769821f1d3SAlan Somers {
4779821f1d3SAlan Somers 	uint64_t ino = 42;
4789821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
4795e633330SAlan Somers 	char attrs[9] = "user.foo";
4809821f1d3SAlan Somers 
4819821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
4825e633330SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
4835e633330SAlan Somers 	{
4845e633330SAlan Somers 		out.body.listxattr.size = sizeof(attrs);
4859821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
4869821f1d3SAlan Somers 	}));
4879821f1d3SAlan Somers 
4885e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs),
4895e633330SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
4905e633330SAlan Somers 			size_t l = sizeof(attrs);
4915e633330SAlan Somers 			strlcpy((char*)out.body.bytes, attrs, l);
4925e633330SAlan Somers 			out.header.len = sizeof(fuse_out_header) + l;
4935e633330SAlan Somers 		})
4945e633330SAlan Somers 	);
4955e633330SAlan Somers 
4965e633330SAlan Somers 	ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
4975e633330SAlan Somers 		<< strerror(errno);
4985e633330SAlan Somers }
4995e633330SAlan Somers 
5005e633330SAlan Somers /*
5015e633330SAlan Somers  * The list of extended attributes grows in between the server's two calls to
5025e633330SAlan Somers  * FUSE_LISTXATTR.
5035e633330SAlan Somers  */
TEST_F(Listxattr,size_only_race_bigger)5045e633330SAlan Somers TEST_F(Listxattr, size_only_race_bigger)
5055e633330SAlan Somers {
5065e633330SAlan Somers 	uint64_t ino = 42;
5075e633330SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
5085e633330SAlan Somers 	char attrs0[9] = "user.foo";
5095e633330SAlan Somers 	char attrs1[18] = "user.foo\0user.bar";
5105e633330SAlan Somers 	Sequence seq;
5115e633330SAlan Somers 
5125e633330SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
5135e633330SAlan Somers 	.WillRepeatedly(Invoke(
5145e633330SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
5155e633330SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
5165e633330SAlan Somers 		out.body.entry.attr.mode = S_IFREG | 0644;
5175e633330SAlan Somers 		out.body.entry.nodeid = ino;
5185e633330SAlan Somers 		out.body.entry.attr.nlink = 1;
5195e633330SAlan Somers 		out.body.entry.attr_valid = UINT64_MAX;
5205e633330SAlan Somers 		out.body.entry.entry_valid = UINT64_MAX;
5215e633330SAlan Somers 	})));
5225e633330SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
5235e633330SAlan Somers 	{
5245e633330SAlan Somers 		out.body.listxattr.size = sizeof(attrs0);
5255e633330SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
5265e633330SAlan Somers 	}), &seq);
5275e633330SAlan Somers 
5285e633330SAlan Somers 	/*
5295e633330SAlan Somers 	 * After the first FUSE_LISTXATTR the list grew, so the second
5305e633330SAlan Somers 	 * operation returns ERANGE.
5315e633330SAlan Somers 	 */
5325e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs0), ReturnErrno(ERANGE), &seq);
5335e633330SAlan Somers 
5345e633330SAlan Somers 	/* And now the kernel retries */
5355e633330SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
5365e633330SAlan Somers 	{
5375e633330SAlan Somers 		out.body.listxattr.size = sizeof(attrs1);
5385e633330SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
5395e633330SAlan Somers 	}), &seq);
5405e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs1),
5415e633330SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
5425e633330SAlan Somers 			memcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
5435e633330SAlan Somers 			out.header.len = sizeof(fuse_out_header) +
5445e633330SAlan Somers 			    sizeof(attrs1);
5455e633330SAlan Somers 		}), &seq
5465e633330SAlan Somers 	);
5475e633330SAlan Somers 
5485e633330SAlan Somers 	/* Userspace should never know about the retry */
5495e633330SAlan Somers 	ASSERT_EQ(8, extattr_list_file(FULLPATH, ns, NULL, 0))
5505e633330SAlan Somers 		<< strerror(errno);
5515e633330SAlan Somers }
5525e633330SAlan Somers 
5535e633330SAlan Somers /*
5545e633330SAlan Somers  * The list of extended attributes shrinks in between the server's two calls to
5555e633330SAlan Somers  * FUSE_LISTXATTR
5565e633330SAlan Somers  */
TEST_F(Listxattr,size_only_race_smaller)5575e633330SAlan Somers TEST_F(Listxattr, size_only_race_smaller)
5585e633330SAlan Somers {
5595e633330SAlan Somers 	uint64_t ino = 42;
5605e633330SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
5615e633330SAlan Somers 	char attrs0[18] = "user.foo\0user.bar";
5625e633330SAlan Somers 	char attrs1[9] = "user.foo";
5635e633330SAlan Somers 
5645e633330SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
5655e633330SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
5665e633330SAlan Somers 	{
5675e633330SAlan Somers 		out.body.listxattr.size = sizeof(attrs0);
5685e633330SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
5695e633330SAlan Somers 	}));
5705e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs0),
5715e633330SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
5725e633330SAlan Somers 			strlcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
5735e633330SAlan Somers 			out.header.len = sizeof(fuse_out_header) +
5745e633330SAlan Somers 			    sizeof(attrs1);
5759821f1d3SAlan Somers 		})
5769821f1d3SAlan Somers 	);
5779821f1d3SAlan Somers 
5789821f1d3SAlan Somers 	ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
5799821f1d3SAlan Somers 		<< strerror(errno);
5809821f1d3SAlan Somers }
5819821f1d3SAlan Somers 
TEST_F(Listxattr,size_only_really_big)5829821f1d3SAlan Somers TEST_F(Listxattr, size_only_really_big)
5839821f1d3SAlan Somers {
5849821f1d3SAlan Somers 	uint64_t ino = 42;
5859821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
5869821f1d3SAlan Somers 
5879821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
58829edc611SAlan Somers 	expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
58929edc611SAlan Somers 		out.body.listxattr.size = 16000;
5909821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, listxattr);
5919821f1d3SAlan Somers 	}));
5929821f1d3SAlan Somers 
5935e633330SAlan Somers 	expect_listxattr(ino, 16000,
59429edc611SAlan Somers 		ReturnImmediate([](auto in __unused, auto& out) {
5959821f1d3SAlan Somers 			const char l[16] = "user.foobarbang";
5969821f1d3SAlan Somers 			for (int i=0; i < 1000; i++) {
59729edc611SAlan Somers 				memcpy(&out.body.bytes[16 * i], l, 16);
5989821f1d3SAlan Somers 			}
59929edc611SAlan Somers 			out.header.len = sizeof(fuse_out_header) + 16000;
6009821f1d3SAlan Somers 		})
6019821f1d3SAlan Somers 	);
6029821f1d3SAlan Somers 
6039821f1d3SAlan Somers 	ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0))
6049821f1d3SAlan Somers 		<< strerror(errno);
6059821f1d3SAlan Somers }
6069821f1d3SAlan Somers 
6079821f1d3SAlan Somers /*
6089821f1d3SAlan Somers  * List all of the user attributes of a file which has both user and system
6099821f1d3SAlan Somers  * attributes
6109821f1d3SAlan Somers  */
TEST_F(Listxattr,user)6119821f1d3SAlan Somers TEST_F(Listxattr, user)
6129821f1d3SAlan Somers {
6139821f1d3SAlan Somers 	uint64_t ino = 42;
6149821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
6159821f1d3SAlan Somers 	char data[80];
6169821f1d3SAlan Somers 	char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'};
6179821f1d3SAlan Somers 	char attrs[28] = "user.foo\0system.x\0user.bang";
6189821f1d3SAlan Somers 
6199821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
6209821f1d3SAlan Somers 	expect_listxattr(ino, 0,
62129edc611SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
62229edc611SAlan Somers 			out.body.listxattr.size = sizeof(attrs);
6239821f1d3SAlan Somers 			SET_OUT_HEADER_LEN(out, listxattr);
6249821f1d3SAlan Somers 		})
6259821f1d3SAlan Somers 	);
6269821f1d3SAlan Somers 
6275e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs),
62829edc611SAlan Somers 	ReturnImmediate([&](auto in __unused, auto& out) {
62929edc611SAlan Somers 		memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
63029edc611SAlan Somers 		out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
6319821f1d3SAlan Somers 	}));
6329821f1d3SAlan Somers 
63329edc611SAlan Somers 	ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
6349821f1d3SAlan Somers 		extattr_list_file(FULLPATH, ns, data, sizeof(data)))
6359821f1d3SAlan Somers 		<< strerror(errno);
6369821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
6379821f1d3SAlan Somers }
6389821f1d3SAlan Somers 
6399821f1d3SAlan Somers /*
6409821f1d3SAlan Somers  * List all of the system attributes of a file which has both user and system
6419821f1d3SAlan Somers  * attributes
6429821f1d3SAlan Somers  */
TEST_F(Listxattr,system)6439821f1d3SAlan Somers TEST_F(Listxattr, system)
6449821f1d3SAlan Somers {
6459821f1d3SAlan Somers 	uint64_t ino = 42;
6469821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
6479821f1d3SAlan Somers 	char data[80];
6489821f1d3SAlan Somers 	char expected[2] = {1, 'x'};
6499821f1d3SAlan Somers 	char attrs[28] = "user.foo\0system.x\0user.bang";
6509821f1d3SAlan Somers 
6519821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
6529821f1d3SAlan Somers 	expect_listxattr(ino, 0,
65329edc611SAlan Somers 		ReturnImmediate([&](auto in __unused, auto& out) {
65429edc611SAlan Somers 			out.body.listxattr.size = sizeof(attrs);
6559821f1d3SAlan Somers 			SET_OUT_HEADER_LEN(out, listxattr);
6569821f1d3SAlan Somers 		})
6579821f1d3SAlan Somers 	);
6589821f1d3SAlan Somers 
6595e633330SAlan Somers 	expect_listxattr(ino, sizeof(attrs),
66029edc611SAlan Somers 	ReturnImmediate([&](auto in __unused, auto& out) {
66129edc611SAlan Somers 		memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
66229edc611SAlan Somers 		out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
6639821f1d3SAlan Somers 	}));
6649821f1d3SAlan Somers 
66529edc611SAlan Somers 	ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
6669821f1d3SAlan Somers 		extattr_list_file(FULLPATH, ns, data, sizeof(data)))
6679821f1d3SAlan Somers 		<< strerror(errno);
6689821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
6699821f1d3SAlan Somers }
6709821f1d3SAlan Somers 
6719821f1d3SAlan Somers /* Fail to remove a nonexistent attribute */
TEST_F(Removexattr,enoattr)6729821f1d3SAlan Somers TEST_F(Removexattr, enoattr)
6739821f1d3SAlan Somers {
6749821f1d3SAlan Somers 	uint64_t ino = 42;
6759821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
6769821f1d3SAlan Somers 
6779821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
6789821f1d3SAlan Somers 	expect_removexattr(ino, "user.foo", ENOATTR);
6799821f1d3SAlan Somers 
6809821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
6819821f1d3SAlan Somers 	ASSERT_EQ(ENOATTR, errno);
6829821f1d3SAlan Somers }
6839821f1d3SAlan Somers 
6849821f1d3SAlan Somers /*
6859821f1d3SAlan Somers  * If the filesystem returns ENOSYS, then it will be treated as a permanent
6869821f1d3SAlan Somers  * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP
6879821f1d3SAlan Somers  * without querying the filesystem daemon
6889821f1d3SAlan Somers  */
TEST_F(Removexattr,enosys)6891f4a83f9SAlan Somers TEST_F(Removexattr, enosys)
6909821f1d3SAlan Somers {
6919821f1d3SAlan Somers 	uint64_t ino = 42;
6929821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
6939821f1d3SAlan Somers 
6941f4a83f9SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
6959821f1d3SAlan Somers 	expect_removexattr(ino, "user.foo", ENOSYS);
6969821f1d3SAlan Somers 
6979821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
6989821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
6999821f1d3SAlan Somers 
7009821f1d3SAlan Somers 	/* Subsequent attempts should not query the filesystem at all */
7019821f1d3SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
7029821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
7039821f1d3SAlan Somers }
7049821f1d3SAlan Somers 
7059821f1d3SAlan Somers /* Successfully remove a user xattr */
TEST_F(Removexattr,user)7069821f1d3SAlan Somers TEST_F(Removexattr, user)
7079821f1d3SAlan Somers {
7089821f1d3SAlan Somers 	uint64_t ino = 42;
7099821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
7109821f1d3SAlan Somers 
7119821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
7129821f1d3SAlan Somers 	expect_removexattr(ino, "user.foo", 0);
7139821f1d3SAlan Somers 
7149821f1d3SAlan Somers 	ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
7159821f1d3SAlan Somers 		<< strerror(errno);
7169821f1d3SAlan Somers }
7179821f1d3SAlan Somers 
7189821f1d3SAlan Somers /* Successfully remove a system xattr */
TEST_F(Removexattr,system)7199821f1d3SAlan Somers TEST_F(Removexattr, system)
7209821f1d3SAlan Somers {
7219821f1d3SAlan Somers 	uint64_t ino = 42;
7229821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
7239821f1d3SAlan Somers 
7249821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
7259821f1d3SAlan Somers 	expect_removexattr(ino, "system.foo", 0);
7269821f1d3SAlan Somers 
7279821f1d3SAlan Somers 	ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
7289821f1d3SAlan Somers 		<< strerror(errno);
7299821f1d3SAlan Somers }
7309821f1d3SAlan Somers 
7319821f1d3SAlan Somers /*
7329821f1d3SAlan Somers  * If the filesystem returns ENOSYS, then it will be treated as a permanent
7339821f1d3SAlan Somers  * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP
7349821f1d3SAlan Somers  * without querying the filesystem daemon
7359821f1d3SAlan Somers  */
TEST_F(Setxattr,enosys)7361f4a83f9SAlan Somers TEST_F(Setxattr, enosys)
7379821f1d3SAlan Somers {
7389821f1d3SAlan Somers 	uint64_t ino = 42;
7399821f1d3SAlan Somers 	const char value[] = "whatever";
7409821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
7419821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
7429821f1d3SAlan Somers 	ssize_t r;
7439821f1d3SAlan Somers 
7441f4a83f9SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
7459821f1d3SAlan Somers 	expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS));
7469821f1d3SAlan Somers 
7475a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
7485a0b9a27SAlan Somers 		value_len);
7499821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
7509821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
7519821f1d3SAlan Somers 
7529821f1d3SAlan Somers 	/* Subsequent attempts should not query the filesystem at all */
7535a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
7545a0b9a27SAlan Somers 		value_len);
7559821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
7569821f1d3SAlan Somers 	EXPECT_EQ(EOPNOTSUPP, errno);
7579821f1d3SAlan Somers }
7589821f1d3SAlan Somers 
7599821f1d3SAlan Somers /*
7609821f1d3SAlan Somers  * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem
7619821f1d3SAlan Somers  * as currently configured doesn't support extended attributes.
7629821f1d3SAlan Somers  */
TEST_F(Setxattr,enotsup)7639821f1d3SAlan Somers TEST_F(Setxattr, enotsup)
7649821f1d3SAlan Somers {
7659821f1d3SAlan Somers 	uint64_t ino = 42;
7669821f1d3SAlan Somers 	const char value[] = "whatever";
7679821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
7689821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
7699821f1d3SAlan Somers 	ssize_t r;
7709821f1d3SAlan Somers 
7719821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
7729821f1d3SAlan Somers 	expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP));
7739821f1d3SAlan Somers 
7745a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
7755a0b9a27SAlan Somers 		value_len);
7769821f1d3SAlan Somers 	ASSERT_EQ(-1, r);
7779821f1d3SAlan Somers 	EXPECT_EQ(ENOTSUP, errno);
7789821f1d3SAlan Somers }
7799821f1d3SAlan Somers 
7809821f1d3SAlan Somers /*
7819821f1d3SAlan Somers  * Successfully set a user attribute.
7829821f1d3SAlan Somers  */
TEST_F(Setxattr,user)7839821f1d3SAlan Somers TEST_F(Setxattr, user)
7849821f1d3SAlan Somers {
7859821f1d3SAlan Somers 	uint64_t ino = 42;
7869821f1d3SAlan Somers 	const char value[] = "whatever";
7879821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
7889821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
7899821f1d3SAlan Somers 	ssize_t r;
7909821f1d3SAlan Somers 
7919821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
7929821f1d3SAlan Somers 	expect_setxattr(ino, "user.foo", value, ReturnErrno(0));
7939821f1d3SAlan Somers 
7945a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
7955a0b9a27SAlan Somers 		value_len);
7969821f1d3SAlan Somers 	ASSERT_EQ(value_len, r) << strerror(errno);
7979821f1d3SAlan Somers }
7989821f1d3SAlan Somers 
7999821f1d3SAlan Somers /*
8009821f1d3SAlan Somers  * Successfully set a system attribute.
8019821f1d3SAlan Somers  */
TEST_F(Setxattr,system)8029821f1d3SAlan Somers TEST_F(Setxattr, system)
8039821f1d3SAlan Somers {
8049821f1d3SAlan Somers 	uint64_t ino = 42;
8059821f1d3SAlan Somers 	const char value[] = "whatever";
8069821f1d3SAlan Somers 	ssize_t value_len = strlen(value) + 1;
8079821f1d3SAlan Somers 	int ns = EXTATTR_NAMESPACE_SYSTEM;
8089821f1d3SAlan Somers 	ssize_t r;
8099821f1d3SAlan Somers 
8109821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
8119821f1d3SAlan Somers 	expect_setxattr(ino, "system.foo", value, ReturnErrno(0));
8129821f1d3SAlan Somers 
8135a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
8145a0b9a27SAlan Somers 		value_len);
8159821f1d3SAlan Somers 	ASSERT_EQ(value_len, r) << strerror(errno);
8169821f1d3SAlan Somers }
817ff4fbdf5SAlan Somers 
TEST_F(RofsXattr,deleteextattr_erofs)818666f8543SAlan Somers TEST_F(RofsXattr, deleteextattr_erofs)
819666f8543SAlan Somers {
820666f8543SAlan Somers 	uint64_t ino = 42;
821666f8543SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
822666f8543SAlan Somers 
823666f8543SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
824666f8543SAlan Somers 
825666f8543SAlan Somers 	ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
826666f8543SAlan Somers 	ASSERT_EQ(EROFS, errno);
827666f8543SAlan Somers }
828666f8543SAlan Somers 
TEST_F(RofsXattr,setextattr_erofs)829666f8543SAlan Somers TEST_F(RofsXattr, setextattr_erofs)
830666f8543SAlan Somers {
831666f8543SAlan Somers 	uint64_t ino = 42;
832666f8543SAlan Somers 	const char value[] = "whatever";
833666f8543SAlan Somers 	ssize_t value_len = strlen(value) + 1;
834666f8543SAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
835666f8543SAlan Somers 	ssize_t r;
836666f8543SAlan Somers 
837666f8543SAlan Somers 	expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
838666f8543SAlan Somers 
8395a0b9a27SAlan Somers 	r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
8405a0b9a27SAlan Somers 		value_len);
841666f8543SAlan Somers 	ASSERT_EQ(-1, r);
842666f8543SAlan Somers 	EXPECT_EQ(EROFS, errno);
843666f8543SAlan Somers }
844