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 <sys/types.h> 339821f1d3SAlan Somers #include <sys/extattr.h> 349821f1d3SAlan Somers #include <string.h> 359821f1d3SAlan Somers } 369821f1d3SAlan Somers 379821f1d3SAlan Somers #include "mockfs.hh" 389821f1d3SAlan Somers #include "utils.hh" 399821f1d3SAlan Somers 409821f1d3SAlan Somers using namespace testing; 419821f1d3SAlan Somers 429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 439821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 449821f1d3SAlan Somers 459821f1d3SAlan Somers /* For testing filesystems without posix locking support */ 469821f1d3SAlan Somers class Xattr: public FuseTest { 479821f1d3SAlan Somers public: 489821f1d3SAlan Somers void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) 499821f1d3SAlan Somers { 509821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 519821f1d3SAlan Somers ResultOf([=](auto in) { 529821f1d3SAlan Somers const char *a = (const char*)in->body.bytes + 539821f1d3SAlan Somers sizeof(fuse_getxattr_in); 549821f1d3SAlan Somers return (in->header.opcode == FUSE_GETXATTR && 559821f1d3SAlan Somers in->header.nodeid == ino && 569821f1d3SAlan Somers 0 == strcmp(attr, a)); 579821f1d3SAlan Somers }, Eq(true)), 589821f1d3SAlan Somers _) 599821f1d3SAlan Somers ).WillOnce(Invoke(r)); 609821f1d3SAlan Somers } 619821f1d3SAlan Somers 629821f1d3SAlan Somers void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r) 639821f1d3SAlan Somers { 649821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 659821f1d3SAlan Somers ResultOf([=](auto in) { 669821f1d3SAlan Somers return (in->header.opcode == FUSE_LISTXATTR && 679821f1d3SAlan Somers in->header.nodeid == ino && 689821f1d3SAlan Somers in->body.listxattr.size == size); 699821f1d3SAlan Somers }, Eq(true)), 709821f1d3SAlan Somers _) 719821f1d3SAlan Somers ).WillOnce(Invoke(r)) 729821f1d3SAlan Somers .RetiresOnSaturation(); 739821f1d3SAlan Somers } 749821f1d3SAlan Somers 759821f1d3SAlan Somers void expect_removexattr(uint64_t ino, const char *attr, int error) 769821f1d3SAlan Somers { 779821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 789821f1d3SAlan Somers ResultOf([=](auto in) { 799821f1d3SAlan Somers const char *a = (const char*)in->body.bytes; 809821f1d3SAlan Somers return (in->header.opcode == FUSE_REMOVEXATTR && 819821f1d3SAlan Somers in->header.nodeid == ino && 829821f1d3SAlan Somers 0 == strcmp(attr, a)); 839821f1d3SAlan Somers }, Eq(true)), 849821f1d3SAlan Somers _) 859821f1d3SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 869821f1d3SAlan Somers } 879821f1d3SAlan Somers 889821f1d3SAlan Somers void expect_setxattr(uint64_t ino, const char *attr, const char *value, 899821f1d3SAlan Somers ProcessMockerT r) 909821f1d3SAlan Somers { 919821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 929821f1d3SAlan Somers ResultOf([=](auto in) { 939821f1d3SAlan Somers const char *a = (const char*)in->body.bytes + 949821f1d3SAlan Somers sizeof(fuse_setxattr_in); 959821f1d3SAlan Somers const char *v = a + strlen(a) + 1; 969821f1d3SAlan Somers return (in->header.opcode == FUSE_SETXATTR && 979821f1d3SAlan Somers in->header.nodeid == ino && 989821f1d3SAlan Somers 0 == strcmp(attr, a) && 999821f1d3SAlan Somers 0 == strcmp(value, v)); 1009821f1d3SAlan Somers }, Eq(true)), 1019821f1d3SAlan Somers _) 1029821f1d3SAlan Somers ).WillOnce(Invoke(r)); 1039821f1d3SAlan Somers } 1049821f1d3SAlan Somers 1059821f1d3SAlan Somers }; 1069821f1d3SAlan Somers 1079821f1d3SAlan Somers class Getxattr: public Xattr {}; 1089821f1d3SAlan Somers class Listxattr: public Xattr {}; 1099821f1d3SAlan Somers class Removexattr: public Xattr {}; 1109821f1d3SAlan Somers class Setxattr: public Xattr {}; 111*666f8543SAlan Somers class RofsXattr: public Xattr { 112*666f8543SAlan Somers public: 113*666f8543SAlan Somers virtual void SetUp() { 114*666f8543SAlan Somers m_ro = true; 115*666f8543SAlan Somers Xattr::SetUp(); 116*666f8543SAlan Somers } 117*666f8543SAlan Somers }; 1189821f1d3SAlan Somers 1199821f1d3SAlan Somers /* 1209821f1d3SAlan Somers * If the extended attribute does not exist on this file, the daemon should 1219821f1d3SAlan Somers * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the 1229821f1d3SAlan Somers * correct errror code) 1239821f1d3SAlan Somers */ 1249821f1d3SAlan Somers TEST_F(Getxattr, enoattr) 1259821f1d3SAlan Somers { 1269821f1d3SAlan Somers char data[80]; 1279821f1d3SAlan Somers uint64_t ino = 42; 1289821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 1299821f1d3SAlan Somers ssize_t r; 1309821f1d3SAlan Somers 1319821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 1329821f1d3SAlan Somers expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR)); 1339821f1d3SAlan Somers 1349821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 1359821f1d3SAlan Somers ASSERT_EQ(-1, r); 1369821f1d3SAlan Somers ASSERT_EQ(ENOATTR, errno); 1379821f1d3SAlan Somers } 1389821f1d3SAlan Somers 1399821f1d3SAlan Somers /* 1409821f1d3SAlan Somers * If the filesystem returns ENOSYS, then it will be treated as a permanent 1419821f1d3SAlan Somers * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP 1429821f1d3SAlan Somers * without querying the filesystem daemon 1439821f1d3SAlan Somers */ 1449821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 1459821f1d3SAlan Somers TEST_F(Getxattr, DISABLED_enosys) 1469821f1d3SAlan Somers { 1479821f1d3SAlan Somers char data[80]; 1489821f1d3SAlan Somers uint64_t ino = 42; 1499821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 1509821f1d3SAlan Somers ssize_t r; 1519821f1d3SAlan Somers 1529821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 1539821f1d3SAlan Somers expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS)); 1549821f1d3SAlan Somers 1559821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 1569821f1d3SAlan Somers ASSERT_EQ(-1, r); 1579821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 1589821f1d3SAlan Somers 1599821f1d3SAlan Somers /* Subsequent attempts should not query the filesystem at all */ 1609821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 1619821f1d3SAlan Somers ASSERT_EQ(-1, r); 1629821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 1639821f1d3SAlan Somers } 1649821f1d3SAlan Somers 1659821f1d3SAlan Somers /* 1669821f1d3SAlan Somers * On FreeBSD, if the user passes an insufficiently large buffer then the 1679821f1d3SAlan Somers * filesystem is supposed to copy as much of the attribute's value as will fit. 1689821f1d3SAlan Somers * 1699821f1d3SAlan Somers * On Linux, however, the filesystem is supposed to return ERANGE. 1709821f1d3SAlan Somers * 1719821f1d3SAlan Somers * libfuse specifies the Linux behavior. However, that's probably an error. 1729821f1d3SAlan Somers * It would probably be correct for the filesystem to use platform-dependent 1739821f1d3SAlan Somers * behavior. 1749821f1d3SAlan Somers * 1759821f1d3SAlan Somers * This test case covers a filesystem that uses the Linux behavior 1769821f1d3SAlan Somers */ 1779821f1d3SAlan Somers TEST_F(Getxattr, erange) 1789821f1d3SAlan Somers { 1799821f1d3SAlan Somers char data[10]; 1809821f1d3SAlan Somers uint64_t ino = 42; 1819821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 1829821f1d3SAlan Somers ssize_t r; 1839821f1d3SAlan Somers 1849821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 1859821f1d3SAlan Somers expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE)); 1869821f1d3SAlan Somers 1879821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 1889821f1d3SAlan Somers ASSERT_EQ(-1, r); 1899821f1d3SAlan Somers ASSERT_EQ(ERANGE, errno); 1909821f1d3SAlan Somers } 1919821f1d3SAlan Somers 1929821f1d3SAlan Somers /* 1939821f1d3SAlan Somers * If the user passes a 0-length buffer, then the daemon should just return the 1949821f1d3SAlan Somers * size of the attribute 1959821f1d3SAlan Somers */ 1969821f1d3SAlan Somers TEST_F(Getxattr, size_only) 1979821f1d3SAlan Somers { 1989821f1d3SAlan Somers uint64_t ino = 42; 1999821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 2009821f1d3SAlan Somers 2019821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 2029821f1d3SAlan Somers expect_getxattr(ino, "user.foo", 2039821f1d3SAlan Somers ReturnImmediate([](auto in __unused, auto out) { 2049821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, getxattr); 2059821f1d3SAlan Somers out->body.getxattr.size = 99; 2069821f1d3SAlan Somers }) 2079821f1d3SAlan Somers ); 2089821f1d3SAlan Somers 2099821f1d3SAlan Somers ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0)) 2109821f1d3SAlan Somers << strerror(errno);; 2119821f1d3SAlan Somers } 2129821f1d3SAlan Somers 2139821f1d3SAlan Somers /* 2149821f1d3SAlan Somers * Successfully get an attribute from the system namespace 2159821f1d3SAlan Somers */ 2169821f1d3SAlan Somers TEST_F(Getxattr, system) 2179821f1d3SAlan Somers { 2189821f1d3SAlan Somers uint64_t ino = 42; 2199821f1d3SAlan Somers char data[80]; 2209821f1d3SAlan Somers const char value[] = "whatever"; 2219821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 2229821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 2239821f1d3SAlan Somers ssize_t r; 2249821f1d3SAlan Somers 2259821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 2269821f1d3SAlan Somers expect_getxattr(ino, "system.foo", 2279821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 2289821f1d3SAlan Somers memcpy((void*)out->body.bytes, value, value_len); 2299821f1d3SAlan Somers out->header.len = sizeof(out->header) + value_len; 2309821f1d3SAlan Somers }) 2319821f1d3SAlan Somers ); 2329821f1d3SAlan Somers 2339821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 2349821f1d3SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 2359821f1d3SAlan Somers EXPECT_STREQ(value, data); 2369821f1d3SAlan Somers } 2379821f1d3SAlan Somers 2389821f1d3SAlan Somers /* 2399821f1d3SAlan Somers * Successfully get an attribute from the user namespace 2409821f1d3SAlan Somers */ 2419821f1d3SAlan Somers TEST_F(Getxattr, user) 2429821f1d3SAlan Somers { 2439821f1d3SAlan Somers uint64_t ino = 42; 2449821f1d3SAlan Somers char data[80]; 2459821f1d3SAlan Somers const char value[] = "whatever"; 2469821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 2479821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 2489821f1d3SAlan Somers ssize_t r; 2499821f1d3SAlan Somers 2509821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 2519821f1d3SAlan Somers expect_getxattr(ino, "user.foo", 2529821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 2539821f1d3SAlan Somers memcpy((void*)out->body.bytes, value, value_len); 2549821f1d3SAlan Somers out->header.len = sizeof(out->header) + value_len; 2559821f1d3SAlan Somers }) 2569821f1d3SAlan Somers ); 2579821f1d3SAlan Somers 2589821f1d3SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 2599821f1d3SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 2609821f1d3SAlan Somers EXPECT_STREQ(value, data); 2619821f1d3SAlan Somers } 2629821f1d3SAlan Somers 2639821f1d3SAlan Somers /* 2649821f1d3SAlan Somers * If the filesystem returns ENOSYS, then it will be treated as a permanent 2659821f1d3SAlan Somers * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP 2669821f1d3SAlan Somers * without querying the filesystem daemon 2679821f1d3SAlan Somers */ 2689821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 2699821f1d3SAlan Somers TEST_F(Listxattr, DISABLED_enosys) 2709821f1d3SAlan Somers { 2719821f1d3SAlan Somers uint64_t ino = 42; 2729821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 2739821f1d3SAlan Somers 2749821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 2759821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnErrno(ENOSYS)); 2769821f1d3SAlan Somers 2779821f1d3SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 2789821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 2799821f1d3SAlan Somers 2809821f1d3SAlan Somers /* Subsequent attempts should not query the filesystem at all */ 2819821f1d3SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 2829821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 2839821f1d3SAlan Somers } 2849821f1d3SAlan Somers 2859821f1d3SAlan Somers /* 2869821f1d3SAlan Somers * Listing extended attributes failed because they aren't configured on this 2879821f1d3SAlan Somers * filesystem 2889821f1d3SAlan Somers */ 2899821f1d3SAlan Somers TEST_F(Listxattr, enotsup) 2909821f1d3SAlan Somers { 2919821f1d3SAlan Somers uint64_t ino = 42; 2929821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 2939821f1d3SAlan Somers 2949821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 2959821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); 2969821f1d3SAlan Somers 2979821f1d3SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 2989821f1d3SAlan Somers ASSERT_EQ(ENOTSUP, errno); 2999821f1d3SAlan Somers } 3009821f1d3SAlan Somers 3019821f1d3SAlan Somers /* 3029821f1d3SAlan Somers * On FreeBSD, if the user passes an insufficiently large buffer then the 3039821f1d3SAlan Somers * filesystem is supposed to copy as much of the attribute's value as will fit. 3049821f1d3SAlan Somers * 3059821f1d3SAlan Somers * On Linux, however, the filesystem is supposed to return ERANGE. 3069821f1d3SAlan Somers * 3079821f1d3SAlan Somers * libfuse specifies the Linux behavior. However, that's probably an error. 3089821f1d3SAlan Somers * It would probably be correct for the filesystem to use platform-dependent 3099821f1d3SAlan Somers * behavior. 3109821f1d3SAlan Somers * 3119821f1d3SAlan Somers * This test case covers a filesystem that uses the Linux behavior 3129821f1d3SAlan Somers */ 3139821f1d3SAlan Somers TEST_F(Listxattr, erange) 3149821f1d3SAlan Somers { 3159821f1d3SAlan Somers uint64_t ino = 42; 3169821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 3179821f1d3SAlan Somers 3189821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 3199821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnErrno(ERANGE)); 3209821f1d3SAlan Somers 3219821f1d3SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 3229821f1d3SAlan Somers ASSERT_EQ(ERANGE, errno); 3239821f1d3SAlan Somers } 3249821f1d3SAlan Somers 3259821f1d3SAlan Somers /* 3269821f1d3SAlan Somers * Get the size of the list that it would take to list no extended attributes 3279821f1d3SAlan Somers */ 3289821f1d3SAlan Somers TEST_F(Listxattr, size_only_empty) 3299821f1d3SAlan Somers { 3309821f1d3SAlan Somers uint64_t ino = 42; 3319821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 3329821f1d3SAlan Somers 3339821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 3349821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { 3359821f1d3SAlan Somers out->body.listxattr.size = 0; 3369821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 3379821f1d3SAlan Somers })); 3389821f1d3SAlan Somers 3399821f1d3SAlan Somers ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) 3409821f1d3SAlan Somers << strerror(errno); 3419821f1d3SAlan Somers } 3429821f1d3SAlan Somers 3439821f1d3SAlan Somers /* 3449821f1d3SAlan Somers * Get the size of the list that it would take to list some extended 3459821f1d3SAlan Somers * attributes. Due to the format differences between a FreeBSD and a 3469821f1d3SAlan Somers * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer 3479821f1d3SAlan Somers * and get the whole list, then convert it, just to figure out its size. 3489821f1d3SAlan Somers */ 3499821f1d3SAlan Somers TEST_F(Listxattr, size_only_nonempty) 3509821f1d3SAlan Somers { 3519821f1d3SAlan Somers uint64_t ino = 42; 3529821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 3539821f1d3SAlan Somers 3549821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 3559821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { 3569821f1d3SAlan Somers out->body.listxattr.size = 45; 3579821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 3589821f1d3SAlan Somers })); 3599821f1d3SAlan Somers 3609821f1d3SAlan Somers // TODO: fix the expected size after fixing the size calculation bug in 3619821f1d3SAlan Somers // fuse_vnop_listextattr. It should be exactly 45. 3629821f1d3SAlan Somers expect_listxattr(ino, 53, 3639821f1d3SAlan Somers ReturnImmediate([](auto in __unused, auto out) { 3649821f1d3SAlan Somers const char l[] = "user.foo"; 3659821f1d3SAlan Somers strlcpy((char*)out->body.bytes, l, 3669821f1d3SAlan Somers sizeof(out->body.bytes)); 3679821f1d3SAlan Somers out->header.len = sizeof(fuse_out_header) + sizeof(l); 3689821f1d3SAlan Somers }) 3699821f1d3SAlan Somers ); 3709821f1d3SAlan Somers 3719821f1d3SAlan Somers ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0)) 3729821f1d3SAlan Somers << strerror(errno); 3739821f1d3SAlan Somers } 3749821f1d3SAlan Somers 3759821f1d3SAlan Somers TEST_F(Listxattr, size_only_really_big) 3769821f1d3SAlan Somers { 3779821f1d3SAlan Somers uint64_t ino = 42; 3789821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 3799821f1d3SAlan Somers 3809821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 3819821f1d3SAlan Somers expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { 3829821f1d3SAlan Somers out->body.listxattr.size = 16000; 3839821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 3849821f1d3SAlan Somers })); 3859821f1d3SAlan Somers 3869821f1d3SAlan Somers // TODO: fix the expected size after fixing the size calculation bug in 3879821f1d3SAlan Somers // fuse_vnop_listextattr. It should be exactly 16000. 3889821f1d3SAlan Somers expect_listxattr(ino, 16008, 3899821f1d3SAlan Somers ReturnImmediate([](auto in __unused, auto out) { 3909821f1d3SAlan Somers const char l[16] = "user.foobarbang"; 3919821f1d3SAlan Somers for (int i=0; i < 1000; i++) { 3929821f1d3SAlan Somers memcpy(&out->body.bytes[16 * i], l, 16); 3939821f1d3SAlan Somers } 3949821f1d3SAlan Somers out->header.len = sizeof(fuse_out_header) + 16000; 3959821f1d3SAlan Somers }) 3969821f1d3SAlan Somers ); 3979821f1d3SAlan Somers 3989821f1d3SAlan Somers ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0)) 3999821f1d3SAlan Somers << strerror(errno); 4009821f1d3SAlan Somers } 4019821f1d3SAlan Somers 4029821f1d3SAlan Somers /* 4039821f1d3SAlan Somers * List all of the user attributes of a file which has both user and system 4049821f1d3SAlan Somers * attributes 4059821f1d3SAlan Somers */ 4069821f1d3SAlan Somers TEST_F(Listxattr, user) 4079821f1d3SAlan Somers { 4089821f1d3SAlan Somers uint64_t ino = 42; 4099821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 4109821f1d3SAlan Somers char data[80]; 4119821f1d3SAlan Somers char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'}; 4129821f1d3SAlan Somers char attrs[28] = "user.foo\0system.x\0user.bang"; 4139821f1d3SAlan Somers 4149821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 4159821f1d3SAlan Somers expect_listxattr(ino, 0, 4169821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 4179821f1d3SAlan Somers out->body.listxattr.size = sizeof(attrs); 4189821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 4199821f1d3SAlan Somers }) 4209821f1d3SAlan Somers ); 4219821f1d3SAlan Somers 4229821f1d3SAlan Somers // TODO: fix the expected size after fixing the size calculation bug in 4239821f1d3SAlan Somers // fuse_vnop_listextattr. 4249821f1d3SAlan Somers expect_listxattr(ino, sizeof(attrs) + 8, 4259821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 4269821f1d3SAlan Somers memcpy((void*)out->body.bytes, attrs, sizeof(attrs)); 4279821f1d3SAlan Somers out->header.len = sizeof(fuse_out_header) + sizeof(attrs); 4289821f1d3SAlan Somers })); 4299821f1d3SAlan Somers 4309821f1d3SAlan Somers ASSERT_EQ((ssize_t)sizeof(expected), 4319821f1d3SAlan Somers extattr_list_file(FULLPATH, ns, data, sizeof(data))) 4329821f1d3SAlan Somers << strerror(errno); 4339821f1d3SAlan Somers ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); 4349821f1d3SAlan Somers } 4359821f1d3SAlan Somers 4369821f1d3SAlan Somers /* 4379821f1d3SAlan Somers * List all of the system attributes of a file which has both user and system 4389821f1d3SAlan Somers * attributes 4399821f1d3SAlan Somers */ 4409821f1d3SAlan Somers TEST_F(Listxattr, system) 4419821f1d3SAlan Somers { 4429821f1d3SAlan Somers uint64_t ino = 42; 4439821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 4449821f1d3SAlan Somers char data[80]; 4459821f1d3SAlan Somers char expected[2] = {1, 'x'}; 4469821f1d3SAlan Somers char attrs[28] = "user.foo\0system.x\0user.bang"; 4479821f1d3SAlan Somers 4489821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 4499821f1d3SAlan Somers expect_listxattr(ino, 0, 4509821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 4519821f1d3SAlan Somers out->body.listxattr.size = sizeof(attrs); 4529821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 4539821f1d3SAlan Somers }) 4549821f1d3SAlan Somers ); 4559821f1d3SAlan Somers 4569821f1d3SAlan Somers // TODO: fix the expected size after fixing the size calculation bug in 4579821f1d3SAlan Somers // fuse_vnop_listextattr. 4589821f1d3SAlan Somers expect_listxattr(ino, sizeof(attrs) + 8, 4599821f1d3SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 4609821f1d3SAlan Somers memcpy((void*)out->body.bytes, attrs, sizeof(attrs)); 4619821f1d3SAlan Somers out->header.len = sizeof(fuse_out_header) + sizeof(attrs); 4629821f1d3SAlan Somers })); 4639821f1d3SAlan Somers 4649821f1d3SAlan Somers ASSERT_EQ((ssize_t)sizeof(expected), 4659821f1d3SAlan Somers extattr_list_file(FULLPATH, ns, data, sizeof(data))) 4669821f1d3SAlan Somers << strerror(errno); 4679821f1d3SAlan Somers ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); 4689821f1d3SAlan Somers } 4699821f1d3SAlan Somers 4709821f1d3SAlan Somers /* Fail to remove a nonexistent attribute */ 4719821f1d3SAlan Somers TEST_F(Removexattr, enoattr) 4729821f1d3SAlan Somers { 4739821f1d3SAlan Somers uint64_t ino = 42; 4749821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 4759821f1d3SAlan Somers 4769821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 4779821f1d3SAlan Somers expect_removexattr(ino, "user.foo", ENOATTR); 4789821f1d3SAlan Somers 4799821f1d3SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 4809821f1d3SAlan Somers ASSERT_EQ(ENOATTR, errno); 4819821f1d3SAlan Somers } 4829821f1d3SAlan Somers 4839821f1d3SAlan Somers /* 4849821f1d3SAlan Somers * If the filesystem returns ENOSYS, then it will be treated as a permanent 4859821f1d3SAlan Somers * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP 4869821f1d3SAlan Somers * without querying the filesystem daemon 4879821f1d3SAlan Somers */ 4889821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 4899821f1d3SAlan Somers TEST_F(Removexattr, DISABLED_enosys) 4909821f1d3SAlan Somers { 4919821f1d3SAlan Somers uint64_t ino = 42; 4929821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 4939821f1d3SAlan Somers 4949821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 4959821f1d3SAlan Somers expect_removexattr(ino, "user.foo", ENOSYS); 4969821f1d3SAlan Somers 4979821f1d3SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 4989821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 4999821f1d3SAlan Somers 5009821f1d3SAlan Somers /* Subsequent attempts should not query the filesystem at all */ 5019821f1d3SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 5029821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 5039821f1d3SAlan Somers } 5049821f1d3SAlan Somers 5059821f1d3SAlan Somers /* Successfully remove a user xattr */ 5069821f1d3SAlan Somers TEST_F(Removexattr, user) 5079821f1d3SAlan Somers { 5089821f1d3SAlan Somers uint64_t ino = 42; 5099821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 5109821f1d3SAlan Somers 5119821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 5129821f1d3SAlan Somers expect_removexattr(ino, "user.foo", 0); 5139821f1d3SAlan Somers 5149821f1d3SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 5159821f1d3SAlan Somers << strerror(errno); 5169821f1d3SAlan Somers } 5179821f1d3SAlan Somers 5189821f1d3SAlan Somers /* Successfully remove a system xattr */ 5199821f1d3SAlan Somers TEST_F(Removexattr, system) 5209821f1d3SAlan Somers { 5219821f1d3SAlan Somers uint64_t ino = 42; 5229821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 5239821f1d3SAlan Somers 5249821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 5259821f1d3SAlan Somers expect_removexattr(ino, "system.foo", 0); 5269821f1d3SAlan Somers 5279821f1d3SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 5289821f1d3SAlan Somers << strerror(errno); 5299821f1d3SAlan Somers } 5309821f1d3SAlan Somers 5319821f1d3SAlan Somers /* 5329821f1d3SAlan Somers * If the filesystem returns ENOSYS, then it will be treated as a permanent 5339821f1d3SAlan Somers * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP 5349821f1d3SAlan Somers * without querying the filesystem daemon 5359821f1d3SAlan Somers */ 5369821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 5379821f1d3SAlan Somers TEST_F(Setxattr, DISABLED_enosys) 5389821f1d3SAlan Somers { 5399821f1d3SAlan Somers uint64_t ino = 42; 5409821f1d3SAlan Somers const char value[] = "whatever"; 5419821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 5429821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 5439821f1d3SAlan Somers ssize_t r; 5449821f1d3SAlan Somers 5459821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 5469821f1d3SAlan Somers expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS)); 5479821f1d3SAlan Somers 5489821f1d3SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 5499821f1d3SAlan Somers ASSERT_EQ(-1, r); 5509821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 5519821f1d3SAlan Somers 5529821f1d3SAlan Somers /* Subsequent attempts should not query the filesystem at all */ 5539821f1d3SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 5549821f1d3SAlan Somers ASSERT_EQ(-1, r); 5559821f1d3SAlan Somers EXPECT_EQ(EOPNOTSUPP, errno); 5569821f1d3SAlan Somers } 5579821f1d3SAlan Somers 5589821f1d3SAlan Somers /* 5599821f1d3SAlan Somers * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem 5609821f1d3SAlan Somers * as currently configured doesn't support extended attributes. 5619821f1d3SAlan Somers */ 5629821f1d3SAlan Somers TEST_F(Setxattr, enotsup) 5639821f1d3SAlan Somers { 5649821f1d3SAlan Somers uint64_t ino = 42; 5659821f1d3SAlan Somers const char value[] = "whatever"; 5669821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 5679821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 5689821f1d3SAlan Somers ssize_t r; 5699821f1d3SAlan Somers 5709821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 5719821f1d3SAlan Somers expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP)); 5729821f1d3SAlan Somers 5739821f1d3SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 5749821f1d3SAlan Somers ASSERT_EQ(-1, r); 5759821f1d3SAlan Somers EXPECT_EQ(ENOTSUP, errno); 5769821f1d3SAlan Somers } 5779821f1d3SAlan Somers 5789821f1d3SAlan Somers /* 5799821f1d3SAlan Somers * Successfully set a user attribute. 5809821f1d3SAlan Somers */ 5819821f1d3SAlan Somers TEST_F(Setxattr, user) 5829821f1d3SAlan Somers { 5839821f1d3SAlan Somers uint64_t ino = 42; 5849821f1d3SAlan Somers const char value[] = "whatever"; 5859821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 5869821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 5879821f1d3SAlan Somers ssize_t r; 5889821f1d3SAlan Somers 5899821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 5909821f1d3SAlan Somers expect_setxattr(ino, "user.foo", value, ReturnErrno(0)); 5919821f1d3SAlan Somers 5929821f1d3SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 5939821f1d3SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 5949821f1d3SAlan Somers } 5959821f1d3SAlan Somers 5969821f1d3SAlan Somers /* 5979821f1d3SAlan Somers * Successfully set a system attribute. 5989821f1d3SAlan Somers */ 5999821f1d3SAlan Somers TEST_F(Setxattr, system) 6009821f1d3SAlan Somers { 6019821f1d3SAlan Somers uint64_t ino = 42; 6029821f1d3SAlan Somers const char value[] = "whatever"; 6039821f1d3SAlan Somers ssize_t value_len = strlen(value) + 1; 6049821f1d3SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 6059821f1d3SAlan Somers ssize_t r; 6069821f1d3SAlan Somers 6079821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 6089821f1d3SAlan Somers expect_setxattr(ino, "system.foo", value, ReturnErrno(0)); 6099821f1d3SAlan Somers 6109821f1d3SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 6119821f1d3SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 6129821f1d3SAlan Somers } 613ff4fbdf5SAlan Somers 614*666f8543SAlan Somers TEST_F(RofsXattr, deleteextattr_erofs) 615*666f8543SAlan Somers { 616*666f8543SAlan Somers uint64_t ino = 42; 617*666f8543SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 618*666f8543SAlan Somers 619*666f8543SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 620*666f8543SAlan Somers 621*666f8543SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 622*666f8543SAlan Somers ASSERT_EQ(EROFS, errno); 623*666f8543SAlan Somers } 624*666f8543SAlan Somers 625*666f8543SAlan Somers TEST_F(RofsXattr, setextattr_erofs) 626*666f8543SAlan Somers { 627*666f8543SAlan Somers uint64_t ino = 42; 628*666f8543SAlan Somers const char value[] = "whatever"; 629*666f8543SAlan Somers ssize_t value_len = strlen(value) + 1; 630*666f8543SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 631*666f8543SAlan Somers ssize_t r; 632*666f8543SAlan Somers 633*666f8543SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 634*666f8543SAlan Somers 635*666f8543SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 636*666f8543SAlan Somers ASSERT_EQ(-1, r); 637*666f8543SAlan Somers EXPECT_EQ(EROFS, errno); 638*666f8543SAlan Somers } 639*666f8543SAlan Somers 640ff4fbdf5SAlan Somers // TODO: EROFS tests 641