2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 /* Tests for all things relating to extended attributes and FUSE */
36 #include <sys/types.h>
37 #include <sys/extattr.h>
39 #include <semaphore.h>
47 using namespace testing;
49 const char FULLPATH[] = "mountpoint/some_file.txt";
50 const char RELPATH[] = "some_file.txt";
51 static sem_t killer_semaphore;
53 void* killer(void* target) {
54 pid_t pid = *(pid_t*)target;
55 sem_wait(&killer_semaphore);
57 printf("Killing! pid %d\n", pid);
63 class Xattr: public FuseTest {
65 void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
69 EXPECT_CALL(*m_mock, process(
70 ResultOf([=](auto in) {
71 return (in.header.opcode == FUSE_LISTXATTR &&
72 in.header.nodeid == ino &&
73 in.body.listxattr.size == size);
77 .RetiresOnSaturation();
79 EXPECT_CALL(*m_mock, process(
80 ResultOf([=](auto in) {
81 return (in.header.opcode == FUSE_LISTXATTR &&
82 in.header.nodeid == ino &&
83 in.body.listxattr.size == size);
88 .RetiresOnSaturation();
92 void expect_removexattr(uint64_t ino, const char *attr, int error)
94 EXPECT_CALL(*m_mock, process(
95 ResultOf([=](auto in) {
96 const char *a = (const char*)in.body.bytes;
97 return (in.header.opcode == FUSE_REMOVEXATTR &&
98 in.header.nodeid == ino &&
99 0 == strcmp(attr, a));
102 ).WillOnce(Invoke(ReturnErrno(error)));
105 void expect_setxattr(uint64_t ino, const char *attr, const char *value,
108 EXPECT_CALL(*m_mock, process(
109 ResultOf([=](auto in) {
110 const char *a = (const char*)in.body.bytes +
111 sizeof(fuse_setxattr_in);
112 const char *v = a + strlen(a) + 1;
113 return (in.header.opcode == FUSE_SETXATTR &&
114 in.header.nodeid == ino &&
115 0 == strcmp(attr, a) &&
116 0 == strcmp(value, v));
119 ).WillOnce(Invoke(r));
124 class Getxattr: public Xattr {};
126 class Listxattr: public Xattr {};
128 /* Listxattr tests that need to use a signal */
129 class ListxattrSig: public Listxattr {
131 pthread_t m_killer_th;
136 * Mount with -o nointr so the mount can't get interrupted while
137 * waiting for a response from the server
142 ASSERT_EQ(0, sem_init(&killer_semaphore, 0, 0)) << strerror(errno);
146 if (m_killer_th != NULL) {
147 pthread_join(m_killer_th, NULL);
150 sem_destroy(&killer_semaphore);
152 FuseTest::TearDown();
156 class Removexattr: public Xattr {};
157 class Setxattr: public Xattr {};
158 class RofsXattr: public Xattr {
160 virtual void SetUp() {
167 * If the extended attribute does not exist on this file, the daemon should
168 * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the
169 * correct errror code)
171 TEST_F(Getxattr, enoattr)
175 int ns = EXTATTR_NAMESPACE_USER;
178 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
179 expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
181 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
183 ASSERT_EQ(ENOATTR, errno);
187 * If the filesystem returns ENOSYS, then it will be treated as a permanent
188 * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP
189 * without querying the filesystem daemon
191 TEST_F(Getxattr, enosys)
195 int ns = EXTATTR_NAMESPACE_USER;
198 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
199 expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS));
201 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
203 EXPECT_EQ(EOPNOTSUPP, errno);
205 /* Subsequent attempts should not query the filesystem at all */
206 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
208 EXPECT_EQ(EOPNOTSUPP, errno);
212 * On FreeBSD, if the user passes an insufficiently large buffer then the
213 * filesystem is supposed to copy as much of the attribute's value as will fit.
215 * On Linux, however, the filesystem is supposed to return ERANGE.
217 * libfuse specifies the Linux behavior. However, that's probably an error.
218 * It would probably be correct for the filesystem to use platform-dependent
221 * This test case covers a filesystem that uses the Linux behavior
222 * TODO: require FreeBSD Behavior.
224 TEST_F(Getxattr, erange)
228 int ns = EXTATTR_NAMESPACE_USER;
231 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
232 expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE));
234 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
236 ASSERT_EQ(ERANGE, errno);
240 * If the user passes a 0-length buffer, then the daemon should just return the
241 * size of the attribute
243 TEST_F(Getxattr, size_only)
246 int ns = EXTATTR_NAMESPACE_USER;
248 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
249 expect_getxattr(ino, "user.foo",
250 ReturnImmediate([](auto in __unused, auto& out) {
251 SET_OUT_HEADER_LEN(out, getxattr);
252 out.body.getxattr.size = 99;
256 ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0))
261 * Successfully get an attribute from the system namespace
263 TEST_F(Getxattr, system)
267 const char value[] = "whatever";
268 ssize_t value_len = strlen(value) + 1;
269 int ns = EXTATTR_NAMESPACE_SYSTEM;
272 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
273 expect_getxattr(ino, "system.foo",
274 ReturnImmediate([&](auto in __unused, auto& out) {
275 memcpy((void*)out.body.bytes, value, value_len);
276 out.header.len = sizeof(out.header) + value_len;
280 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
281 ASSERT_EQ(value_len, r) << strerror(errno);
282 EXPECT_STREQ(value, data);
286 * Successfully get an attribute from the user namespace
288 TEST_F(Getxattr, user)
292 const char value[] = "whatever";
293 ssize_t value_len = strlen(value) + 1;
294 int ns = EXTATTR_NAMESPACE_USER;
297 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
298 expect_getxattr(ino, "user.foo",
299 ReturnImmediate([&](auto in __unused, auto& out) {
300 memcpy((void*)out.body.bytes, value, value_len);
301 out.header.len = sizeof(out.header) + value_len;
305 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
306 ASSERT_EQ(value_len, r) << strerror(errno);
307 EXPECT_STREQ(value, data);
311 * If the filesystem returns ENOSYS, then it will be treated as a permanent
312 * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP
313 * without querying the filesystem daemon
315 TEST_F(Listxattr, enosys)
318 int ns = EXTATTR_NAMESPACE_USER;
320 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
321 expect_listxattr(ino, 0, ReturnErrno(ENOSYS));
323 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
324 EXPECT_EQ(EOPNOTSUPP, errno);
326 /* Subsequent attempts should not query the filesystem at all */
327 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
328 EXPECT_EQ(EOPNOTSUPP, errno);
332 * Listing extended attributes failed because they aren't configured on this
335 TEST_F(Listxattr, enotsup)
338 int ns = EXTATTR_NAMESPACE_USER;
340 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
341 expect_listxattr(ino, 0, ReturnErrno(ENOTSUP));
343 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0));
344 ASSERT_EQ(ENOTSUP, errno);
348 * On FreeBSD, if the user passes an insufficiently large buffer to
349 * extattr_list_file(2) or VOP_LISTEXTATTR(9), then the file system is supposed
350 * to copy as much of the attribute's value as will fit.
352 * On Linux, however, the file system is supposed to return ERANGE if an
353 * insufficiently large buffer is passed to listxattr(2).
355 * fusefs(5) must guarantee the usual FreeBSD behavior.
357 TEST_F(Listxattr, erange)
360 int ns = EXTATTR_NAMESPACE_USER;
361 char attrs[9] = "user.foo";
362 char expected[3] = {3, 'f', 'o'};
365 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
366 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
368 out.body.listxattr.size = sizeof(attrs);
369 SET_OUT_HEADER_LEN(out, listxattr);
371 expect_listxattr(ino, sizeof(attrs),
372 ReturnImmediate([&](auto in __unused, auto& out) {
373 memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
374 out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
378 ASSERT_EQ(static_cast<ssize_t>(sizeof(buf)),
379 extattr_list_file(FULLPATH, ns, buf, sizeof(buf)));
380 ASSERT_EQ(0, memcmp(expected, buf, sizeof(buf)));
384 * A buggy or malicious file system always returns ERANGE, even if we pass an
385 * appropriately sized buffer. That will send the kernel into an infinite
386 * loop. This test will ensure that the loop is interruptible by killing the
387 * blocked process with SIGINT.
389 TEST_F(ListxattrSig, erange_forever)
392 uint32_t lie_size = 10;
395 fork(false, &status, [&] {
396 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
397 .WillRepeatedly(Invoke(
398 ReturnImmediate([=](auto in __unused, auto& out) {
399 SET_OUT_HEADER_LEN(out, entry);
400 out.body.entry.attr.mode = S_IFREG | 0644;
401 out.body.entry.nodeid = ino;
402 out.body.entry.attr.nlink = 1;
403 out.body.entry.attr_valid = UINT64_MAX;
404 out.body.entry.entry_valid = UINT64_MAX;
406 EXPECT_CALL(*m_mock, process(
407 ResultOf([=](auto in) {
408 return (in.header.opcode == FUSE_LISTXATTR &&
409 in.header.nodeid == ino &&
410 in.body.listxattr.size == 0);
413 ).WillRepeatedly(ReturnImmediate([=](auto i __unused, auto& out)
415 /* The file system requests 10 bytes, but it's a lie */
416 out.body.listxattr.size = lie_size;
417 SET_OUT_HEADER_LEN(out, listxattr);
419 * We can send the signal any time after fusefs enters
422 sem_post(&killer_semaphore);
425 * Even though the kernel faithfully respects our size request,
426 * we'll return ERANGE anyway.
428 EXPECT_CALL(*m_mock, process(
429 ResultOf([=](auto in) {
430 return (in.header.opcode == FUSE_LISTXATTR &&
431 in.header.nodeid == ino &&
432 in.body.listxattr.size == lie_size);
435 ).WillRepeatedly(ReturnErrno(ERANGE));
437 ASSERT_EQ(0, pthread_create(&m_killer_th, NULL, killer,
438 &m_mock->m_child_pid))
442 /* Child process will block until it gets signaled */
443 int ns = EXTATTR_NAMESPACE_USER;
445 extattr_list_file(FULLPATH, ns, buf, sizeof(buf));
450 ASSERT_TRUE(WIFSIGNALED(status));
454 * Get the size of the list that it would take to list no extended attributes
456 TEST_F(Listxattr, size_only_empty)
459 int ns = EXTATTR_NAMESPACE_USER;
461 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
462 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
463 out.body.listxattr.size = 0;
464 SET_OUT_HEADER_LEN(out, listxattr);
467 ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0))
472 * Get the size of the list that it would take to list some extended
473 * attributes. Due to the format differences between a FreeBSD and a
474 * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer
475 * and get the whole list, then convert it, just to figure out its size.
477 TEST_F(Listxattr, size_only_nonempty)
480 int ns = EXTATTR_NAMESPACE_USER;
481 char attrs[9] = "user.foo";
483 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
484 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
486 out.body.listxattr.size = sizeof(attrs);
487 SET_OUT_HEADER_LEN(out, listxattr);
490 expect_listxattr(ino, sizeof(attrs),
491 ReturnImmediate([=](auto in __unused, auto& out) {
492 size_t l = sizeof(attrs);
493 strlcpy((char*)out.body.bytes, attrs, l);
494 out.header.len = sizeof(fuse_out_header) + l;
498 ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
503 * The list of extended attributes grows in between the server's two calls to
506 TEST_F(Listxattr, size_only_race_bigger)
509 int ns = EXTATTR_NAMESPACE_USER;
510 char attrs0[9] = "user.foo";
511 char attrs1[18] = "user.foo\0user.bar";
514 EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
515 .WillRepeatedly(Invoke(
516 ReturnImmediate([=](auto in __unused, auto& out) {
517 SET_OUT_HEADER_LEN(out, entry);
518 out.body.entry.attr.mode = S_IFREG | 0644;
519 out.body.entry.nodeid = ino;
520 out.body.entry.attr.nlink = 1;
521 out.body.entry.attr_valid = UINT64_MAX;
522 out.body.entry.entry_valid = UINT64_MAX;
524 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
526 out.body.listxattr.size = sizeof(attrs0);
527 SET_OUT_HEADER_LEN(out, listxattr);
531 * After the first FUSE_LISTXATTR the list grew, so the second
532 * operation returns ERANGE.
534 expect_listxattr(ino, sizeof(attrs0), ReturnErrno(ERANGE), &seq);
536 /* And now the kernel retries */
537 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
539 out.body.listxattr.size = sizeof(attrs1);
540 SET_OUT_HEADER_LEN(out, listxattr);
542 expect_listxattr(ino, sizeof(attrs1),
543 ReturnImmediate([&](auto in __unused, auto& out) {
544 memcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
545 out.header.len = sizeof(fuse_out_header) +
550 /* Userspace should never know about the retry */
551 ASSERT_EQ(8, extattr_list_file(FULLPATH, ns, NULL, 0))
556 * The list of extended attributes shrinks in between the server's two calls to
559 TEST_F(Listxattr, size_only_race_smaller)
562 int ns = EXTATTR_NAMESPACE_USER;
563 char attrs0[18] = "user.foo\0user.bar";
564 char attrs1[9] = "user.foo";
566 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
567 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out)
569 out.body.listxattr.size = sizeof(attrs0);
570 SET_OUT_HEADER_LEN(out, listxattr);
572 expect_listxattr(ino, sizeof(attrs0),
573 ReturnImmediate([&](auto in __unused, auto& out) {
574 strlcpy((char*)out.body.bytes, attrs1, sizeof(attrs1));
575 out.header.len = sizeof(fuse_out_header) +
580 ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0))
584 TEST_F(Listxattr, size_only_really_big)
587 int ns = EXTATTR_NAMESPACE_USER;
589 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
590 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) {
591 out.body.listxattr.size = 16000;
592 SET_OUT_HEADER_LEN(out, listxattr);
595 expect_listxattr(ino, 16000,
596 ReturnImmediate([](auto in __unused, auto& out) {
597 const char l[16] = "user.foobarbang";
598 for (int i=0; i < 1000; i++) {
599 memcpy(&out.body.bytes[16 * i], l, 16);
601 out.header.len = sizeof(fuse_out_header) + 16000;
605 ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0))
610 * List all of the user attributes of a file which has both user and system
613 TEST_F(Listxattr, user)
616 int ns = EXTATTR_NAMESPACE_USER;
618 char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'};
619 char attrs[28] = "user.foo\0system.x\0user.bang";
621 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
622 expect_listxattr(ino, 0,
623 ReturnImmediate([&](auto in __unused, auto& out) {
624 out.body.listxattr.size = sizeof(attrs);
625 SET_OUT_HEADER_LEN(out, listxattr);
629 expect_listxattr(ino, sizeof(attrs),
630 ReturnImmediate([&](auto in __unused, auto& out) {
631 memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
632 out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
635 ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
636 extattr_list_file(FULLPATH, ns, data, sizeof(data)))
638 ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
642 * List all of the system attributes of a file which has both user and system
645 TEST_F(Listxattr, system)
648 int ns = EXTATTR_NAMESPACE_SYSTEM;
650 char expected[2] = {1, 'x'};
651 char attrs[28] = "user.foo\0system.x\0user.bang";
653 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
654 expect_listxattr(ino, 0,
655 ReturnImmediate([&](auto in __unused, auto& out) {
656 out.body.listxattr.size = sizeof(attrs);
657 SET_OUT_HEADER_LEN(out, listxattr);
661 expect_listxattr(ino, sizeof(attrs),
662 ReturnImmediate([&](auto in __unused, auto& out) {
663 memcpy((void*)out.body.bytes, attrs, sizeof(attrs));
664 out.header.len = sizeof(fuse_out_header) + sizeof(attrs);
667 ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)),
668 extattr_list_file(FULLPATH, ns, data, sizeof(data)))
670 ASSERT_EQ(0, memcmp(expected, data, sizeof(expected)));
673 /* Fail to remove a nonexistent attribute */
674 TEST_F(Removexattr, enoattr)
677 int ns = EXTATTR_NAMESPACE_USER;
679 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
680 expect_removexattr(ino, "user.foo", ENOATTR);
682 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
683 ASSERT_EQ(ENOATTR, errno);
687 * If the filesystem returns ENOSYS, then it will be treated as a permanent
688 * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP
689 * without querying the filesystem daemon
691 TEST_F(Removexattr, enosys)
694 int ns = EXTATTR_NAMESPACE_USER;
696 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
697 expect_removexattr(ino, "user.foo", ENOSYS);
699 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
700 EXPECT_EQ(EOPNOTSUPP, errno);
702 /* Subsequent attempts should not query the filesystem at all */
703 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
704 EXPECT_EQ(EOPNOTSUPP, errno);
707 /* Successfully remove a user xattr */
708 TEST_F(Removexattr, user)
711 int ns = EXTATTR_NAMESPACE_USER;
713 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
714 expect_removexattr(ino, "user.foo", 0);
716 ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
720 /* Successfully remove a system xattr */
721 TEST_F(Removexattr, system)
724 int ns = EXTATTR_NAMESPACE_SYSTEM;
726 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
727 expect_removexattr(ino, "system.foo", 0);
729 ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo"))
734 * If the filesystem returns ENOSYS, then it will be treated as a permanent
735 * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP
736 * without querying the filesystem daemon
738 TEST_F(Setxattr, enosys)
741 const char value[] = "whatever";
742 ssize_t value_len = strlen(value) + 1;
743 int ns = EXTATTR_NAMESPACE_USER;
746 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2);
747 expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS));
749 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
752 EXPECT_EQ(EOPNOTSUPP, errno);
754 /* Subsequent attempts should not query the filesystem at all */
755 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
758 EXPECT_EQ(EOPNOTSUPP, errno);
762 * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem
763 * as currently configured doesn't support extended attributes.
765 TEST_F(Setxattr, enotsup)
768 const char value[] = "whatever";
769 ssize_t value_len = strlen(value) + 1;
770 int ns = EXTATTR_NAMESPACE_USER;
773 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
774 expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP));
776 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
779 EXPECT_EQ(ENOTSUP, errno);
783 * Successfully set a user attribute.
785 TEST_F(Setxattr, user)
788 const char value[] = "whatever";
789 ssize_t value_len = strlen(value) + 1;
790 int ns = EXTATTR_NAMESPACE_USER;
793 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
794 expect_setxattr(ino, "user.foo", value, ReturnErrno(0));
796 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
798 ASSERT_EQ(value_len, r) << strerror(errno);
802 * Successfully set a system attribute.
804 TEST_F(Setxattr, system)
807 const char value[] = "whatever";
808 ssize_t value_len = strlen(value) + 1;
809 int ns = EXTATTR_NAMESPACE_SYSTEM;
812 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
813 expect_setxattr(ino, "system.foo", value, ReturnErrno(0));
815 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
817 ASSERT_EQ(value_len, r) << strerror(errno);
820 TEST_F(RofsXattr, deleteextattr_erofs)
823 int ns = EXTATTR_NAMESPACE_USER;
825 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
827 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo"));
828 ASSERT_EQ(EROFS, errno);
831 TEST_F(RofsXattr, setextattr_erofs)
834 const char value[] = "whatever";
835 ssize_t value_len = strlen(value) + 1;
836 int ns = EXTATTR_NAMESPACE_USER;
839 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1);
841 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value,
844 EXPECT_EQ(EROFS, errno);