Changeset View
Changeset View
Standalone View
Standalone View
head/tests/sys/fs/fusefs/xattr.cc
Show All 29 Lines | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
/* Tests for all things relating to extended attributes and FUSE */ | /* Tests for all things relating to extended attributes and FUSE */ | ||||
extern "C" { | extern "C" { | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/extattr.h> | #include <sys/extattr.h> | ||||
#include <sys/wait.h> | |||||
#include <semaphore.h> | |||||
#include <signal.h> | |||||
#include <string.h> | #include <string.h> | ||||
} | } | ||||
#include "mockfs.hh" | #include "mockfs.hh" | ||||
#include "utils.hh" | #include "utils.hh" | ||||
using namespace testing; | using namespace testing; | ||||
const char FULLPATH[] = "mountpoint/some_file.txt"; | const char FULLPATH[] = "mountpoint/some_file.txt"; | ||||
const char RELPATH[] = "some_file.txt"; | const char RELPATH[] = "some_file.txt"; | ||||
static sem_t killer_semaphore; | |||||
void* killer(void* target) { | |||||
pid_t pid = *(pid_t*)target; | |||||
sem_wait(&killer_semaphore); | |||||
if (verbosity > 1) | |||||
printf("Killing! pid %d\n", pid); | |||||
kill(pid, SIGINT); | |||||
return(NULL); | |||||
} | |||||
class Xattr: public FuseTest { | class Xattr: public FuseTest { | ||||
public: | public: | ||||
void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) | void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) | ||||
{ | { | ||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
const char *a = (const char*)in.body.bytes + | const char *a = (const char*)in.body.bytes + | ||||
sizeof(fuse_getxattr_in); | sizeof(fuse_getxattr_in); | ||||
return (in.header.opcode == FUSE_GETXATTR && | return (in.header.opcode == FUSE_GETXATTR && | ||||
in.header.nodeid == ino && | in.header.nodeid == ino && | ||||
0 == strcmp(attr, a)); | 0 == strcmp(attr, a)); | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
).WillOnce(Invoke(r)); | ).WillOnce(Invoke(r)); | ||||
} | } | ||||
void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r) | void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r, | ||||
Sequence *seq = NULL) | |||||
{ | { | ||||
if (seq == NULL) { | |||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
return (in.header.opcode == FUSE_LISTXATTR && | return (in.header.opcode == FUSE_LISTXATTR && | ||||
in.header.nodeid == ino && | in.header.nodeid == ino && | ||||
in.body.listxattr.size == size); | in.body.listxattr.size == size); | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
).WillOnce(Invoke(r)) | ).WillOnce(Invoke(r)) | ||||
.RetiresOnSaturation(); | .RetiresOnSaturation(); | ||||
} else { | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_LISTXATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.listxattr.size == size); | |||||
}, Eq(true)), | |||||
_) | |||||
).InSequence(*seq) | |||||
.WillOnce(Invoke(r)) | |||||
.RetiresOnSaturation(); | |||||
} | } | ||||
} | |||||
void expect_removexattr(uint64_t ino, const char *attr, int error) | void expect_removexattr(uint64_t ino, const char *attr, int error) | ||||
{ | { | ||||
EXPECT_CALL(*m_mock, process( | EXPECT_CALL(*m_mock, process( | ||||
ResultOf([=](auto in) { | ResultOf([=](auto in) { | ||||
const char *a = (const char*)in.body.bytes; | const char *a = (const char*)in.body.bytes; | ||||
return (in.header.opcode == FUSE_REMOVEXATTR && | return (in.header.opcode == FUSE_REMOVEXATTR && | ||||
in.header.nodeid == ino && | in.header.nodeid == ino && | ||||
Show All 18 Lines | EXPECT_CALL(*m_mock, process( | ||||
}, Eq(true)), | }, Eq(true)), | ||||
_) | _) | ||||
).WillOnce(Invoke(r)); | ).WillOnce(Invoke(r)); | ||||
} | } | ||||
}; | }; | ||||
class Getxattr: public Xattr {}; | class Getxattr: public Xattr {}; | ||||
class Listxattr: public Xattr {}; | class Listxattr: public Xattr {}; | ||||
/* Listxattr tests that need to use a signal */ | |||||
class ListxattrSig: public Listxattr { | |||||
public: | |||||
pthread_t m_killer_th; | |||||
pid_t m_child; | |||||
void SetUp() { | |||||
/* | |||||
* Mount with -o nointr so the mount can't get interrupted while | |||||
* waiting for a response from the server | |||||
*/ | |||||
m_nointr = true; | |||||
FuseTest::SetUp(); | |||||
ASSERT_EQ(0, sem_init(&killer_semaphore, 0, 0)) << strerror(errno); | |||||
} | |||||
void TearDown() { | |||||
if (m_killer_th != NULL) { | |||||
pthread_join(m_killer_th, NULL); | |||||
} | |||||
sem_destroy(&killer_semaphore); | |||||
FuseTest::TearDown(); | |||||
} | |||||
}; | |||||
class Removexattr: public Xattr {}; | class Removexattr: public Xattr {}; | ||||
class Setxattr: public Xattr {}; | class Setxattr: public Xattr {}; | ||||
class RofsXattr: public Xattr { | class RofsXattr: public Xattr { | ||||
public: | public: | ||||
virtual void SetUp() { | virtual void SetUp() { | ||||
m_ro = true; | m_ro = true; | ||||
Xattr::SetUp(); | Xattr::SetUp(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* On Linux, however, the filesystem is supposed to return ERANGE. | * On Linux, however, the filesystem is supposed to return ERANGE. | ||||
* | * | ||||
* libfuse specifies the Linux behavior. However, that's probably an error. | * libfuse specifies the Linux behavior. However, that's probably an error. | ||||
* It would probably be correct for the filesystem to use platform-dependent | * It would probably be correct for the filesystem to use platform-dependent | ||||
* behavior. | * behavior. | ||||
* | * | ||||
* This test case covers a filesystem that uses the Linux behavior | * This test case covers a filesystem that uses the Linux behavior | ||||
* TODO: require FreeBSD Behavior. | |||||
*/ | */ | ||||
TEST_F(Getxattr, erange) | TEST_F(Getxattr, erange) | ||||
{ | { | ||||
char data[10]; | char data[10]; | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int ns = EXTATTR_NAMESPACE_USER; | int ns = EXTATTR_NAMESPACE_USER; | ||||
ssize_t r; | ssize_t r; | ||||
▲ Show 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | TEST_F(Listxattr, enotsup) | ||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); | expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); | ||||
ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); | ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); | ||||
ASSERT_EQ(ENOTSUP, errno); | ASSERT_EQ(ENOTSUP, errno); | ||||
} | } | ||||
/* | /* | ||||
* On FreeBSD, if the user passes an insufficiently large buffer then the | * On FreeBSD, if the user passes an insufficiently large buffer to | ||||
* filesystem is supposed to copy as much of the attribute's value as will fit. | * extattr_list_file(2) or VOP_LISTEXTATTR(9), then the file system is supposed | ||||
* to copy as much of the attribute's value as will fit. | |||||
* | * | ||||
* On Linux, however, the filesystem is supposed to return ERANGE. | * On Linux, however, the file system is supposed to return ERANGE if an | ||||
* insufficiently large buffer is passed to listxattr(2). | |||||
* | * | ||||
* libfuse specifies the Linux behavior. However, that's probably an error. | * fusefs(5) must guarantee the usual FreeBSD behavior. | ||||
* It would probably be correct for the filesystem to use platform-dependent | |||||
* behavior. | |||||
* | |||||
* This test case covers a filesystem that uses the Linux behavior | |||||
*/ | */ | ||||
TEST_F(Listxattr, erange) | TEST_F(Listxattr, erange) | ||||
{ | { | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int ns = EXTATTR_NAMESPACE_USER; | int ns = EXTATTR_NAMESPACE_USER; | ||||
char attrs[9] = "user.foo"; | |||||
char expected[3] = {3, 'f', 'o'}; | |||||
char buf[3]; | |||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, ReturnErrno(ERANGE)); | expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) | ||||
{ | |||||
out.body.listxattr.size = sizeof(attrs); | |||||
SET_OUT_HEADER_LEN(out, listxattr); | |||||
})); | |||||
expect_listxattr(ino, sizeof(attrs), | |||||
ReturnImmediate([&](auto in __unused, auto& out) { | |||||
memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); | |||||
out.header.len = sizeof(fuse_out_header) + sizeof(attrs); | |||||
})); | |||||
ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); | |||||
ASSERT_EQ(ERANGE, errno); | ASSERT_EQ(static_cast<ssize_t>(sizeof(buf)), | ||||
extattr_list_file(FULLPATH, ns, buf, sizeof(buf))); | |||||
ASSERT_EQ(0, memcmp(expected, buf, sizeof(buf))); | |||||
} | } | ||||
/* | /* | ||||
* A buggy or malicious file system always returns ERANGE, even if we pass an | |||||
* appropriately sized buffer. That will send the kernel into an infinite | |||||
* loop. This test will ensure that the loop is interruptible by killing the | |||||
* blocked process with SIGINT. | |||||
*/ | |||||
TEST_F(ListxattrSig, erange_forever) | |||||
{ | |||||
uint64_t ino = 42; | |||||
uint32_t lie_size = 10; | |||||
int status; | |||||
fork(false, &status, [&] { | |||||
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) | |||||
.WillRepeatedly(Invoke( | |||||
ReturnImmediate([=](auto in __unused, auto& out) { | |||||
SET_OUT_HEADER_LEN(out, entry); | |||||
out.body.entry.attr.mode = S_IFREG | 0644; | |||||
out.body.entry.nodeid = ino; | |||||
out.body.entry.attr.nlink = 1; | |||||
out.body.entry.attr_valid = UINT64_MAX; | |||||
out.body.entry.entry_valid = UINT64_MAX; | |||||
}))); | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_LISTXATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.listxattr.size == 0); | |||||
}, Eq(true)), | |||||
_) | |||||
).WillRepeatedly(ReturnImmediate([=](auto i __unused, auto& out) | |||||
{ | |||||
/* The file system requests 10 bytes, but it's a lie */ | |||||
out.body.listxattr.size = lie_size; | |||||
SET_OUT_HEADER_LEN(out, listxattr); | |||||
/* | |||||
* We can send the signal any time after fusefs enters | |||||
* VOP_LISTEXTATTR | |||||
*/ | |||||
sem_post(&killer_semaphore); | |||||
})); | |||||
/* | |||||
* Even though the kernel faithfully respects our size request, | |||||
* we'll return ERANGE anyway. | |||||
*/ | |||||
EXPECT_CALL(*m_mock, process( | |||||
ResultOf([=](auto in) { | |||||
return (in.header.opcode == FUSE_LISTXATTR && | |||||
in.header.nodeid == ino && | |||||
in.body.listxattr.size == lie_size); | |||||
}, Eq(true)), | |||||
_) | |||||
).WillRepeatedly(ReturnErrno(ERANGE)); | |||||
ASSERT_EQ(0, pthread_create(&m_killer_th, NULL, killer, | |||||
&m_mock->m_child_pid)) | |||||
<< strerror(errno); | |||||
}, [] { | |||||
/* Child process will block until it gets signaled */ | |||||
int ns = EXTATTR_NAMESPACE_USER; | |||||
char buf[3]; | |||||
extattr_list_file(FULLPATH, ns, buf, sizeof(buf)); | |||||
return 0; | |||||
} | |||||
); | |||||
ASSERT_TRUE(WIFSIGNALED(status)); | |||||
} | |||||
/* | |||||
* Get the size of the list that it would take to list no extended attributes | * Get the size of the list that it would take to list no extended attributes | ||||
*/ | */ | ||||
TEST_F(Listxattr, size_only_empty) | TEST_F(Listxattr, size_only_empty) | ||||
{ | { | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int ns = EXTATTR_NAMESPACE_USER; | int ns = EXTATTR_NAMESPACE_USER; | ||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
Show All 11 Lines | |||||
* attributes. Due to the format differences between a FreeBSD and a | * attributes. Due to the format differences between a FreeBSD and a | ||||
* Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer | * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer | ||||
* and get the whole list, then convert it, just to figure out its size. | * and get the whole list, then convert it, just to figure out its size. | ||||
*/ | */ | ||||
TEST_F(Listxattr, size_only_nonempty) | TEST_F(Listxattr, size_only_nonempty) | ||||
{ | { | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int ns = EXTATTR_NAMESPACE_USER; | int ns = EXTATTR_NAMESPACE_USER; | ||||
char attrs[9] = "user.foo"; | |||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { | expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) | ||||
out.body.listxattr.size = 45; | { | ||||
out.body.listxattr.size = sizeof(attrs); | |||||
SET_OUT_HEADER_LEN(out, listxattr); | SET_OUT_HEADER_LEN(out, listxattr); | ||||
})); | })); | ||||
// TODO: fix the expected size after fixing the size calculation bug in | expect_listxattr(ino, sizeof(attrs), | ||||
// fuse_vnop_listextattr. It should be exactly 45. | ReturnImmediate([=](auto in __unused, auto& out) { | ||||
expect_listxattr(ino, 53, | size_t l = sizeof(attrs); | ||||
ReturnImmediate([](auto in __unused, auto& out) { | strlcpy((char*)out.body.bytes, attrs, l); | ||||
const char l[] = "user.foo"; | out.header.len = sizeof(fuse_out_header) + l; | ||||
strlcpy((char*)out.body.bytes, l, | |||||
sizeof(out.body.bytes)); | |||||
out.header.len = sizeof(fuse_out_header) + sizeof(l); | |||||
}) | }) | ||||
); | ); | ||||
ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0)) | ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0)) | ||||
<< strerror(errno); | << strerror(errno); | ||||
} | } | ||||
/* | |||||
* The list of extended attributes grows in between the server's two calls to | |||||
* FUSE_LISTXATTR. | |||||
*/ | |||||
TEST_F(Listxattr, size_only_race_bigger) | |||||
{ | |||||
uint64_t ino = 42; | |||||
int ns = EXTATTR_NAMESPACE_USER; | |||||
char attrs0[9] = "user.foo"; | |||||
char attrs1[18] = "user.foo\0user.bar"; | |||||
Sequence seq; | |||||
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) | |||||
.WillRepeatedly(Invoke( | |||||
ReturnImmediate([=](auto in __unused, auto& out) { | |||||
SET_OUT_HEADER_LEN(out, entry); | |||||
out.body.entry.attr.mode = S_IFREG | 0644; | |||||
out.body.entry.nodeid = ino; | |||||
out.body.entry.attr.nlink = 1; | |||||
out.body.entry.attr_valid = UINT64_MAX; | |||||
out.body.entry.entry_valid = UINT64_MAX; | |||||
}))); | |||||
expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) | |||||
{ | |||||
out.body.listxattr.size = sizeof(attrs0); | |||||
SET_OUT_HEADER_LEN(out, listxattr); | |||||
}), &seq); | |||||
/* | |||||
* After the first FUSE_LISTXATTR the list grew, so the second | |||||
* operation returns ERANGE. | |||||
*/ | |||||
expect_listxattr(ino, sizeof(attrs0), ReturnErrno(ERANGE), &seq); | |||||
/* And now the kernel retries */ | |||||
expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) | |||||
{ | |||||
out.body.listxattr.size = sizeof(attrs1); | |||||
SET_OUT_HEADER_LEN(out, listxattr); | |||||
}), &seq); | |||||
expect_listxattr(ino, sizeof(attrs1), | |||||
ReturnImmediate([&](auto in __unused, auto& out) { | |||||
memcpy((char*)out.body.bytes, attrs1, sizeof(attrs1)); | |||||
out.header.len = sizeof(fuse_out_header) + | |||||
sizeof(attrs1); | |||||
}), &seq | |||||
); | |||||
/* Userspace should never know about the retry */ | |||||
ASSERT_EQ(8, extattr_list_file(FULLPATH, ns, NULL, 0)) | |||||
<< strerror(errno); | |||||
} | |||||
/* | |||||
* The list of extended attributes shrinks in between the server's two calls to | |||||
* FUSE_LISTXATTR | |||||
*/ | |||||
TEST_F(Listxattr, size_only_race_smaller) | |||||
{ | |||||
uint64_t ino = 42; | |||||
int ns = EXTATTR_NAMESPACE_USER; | |||||
char attrs0[18] = "user.foo\0user.bar"; | |||||
char attrs1[9] = "user.foo"; | |||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | |||||
expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) | |||||
{ | |||||
out.body.listxattr.size = sizeof(attrs0); | |||||
SET_OUT_HEADER_LEN(out, listxattr); | |||||
})); | |||||
expect_listxattr(ino, sizeof(attrs0), | |||||
ReturnImmediate([&](auto in __unused, auto& out) { | |||||
strlcpy((char*)out.body.bytes, attrs1, sizeof(attrs1)); | |||||
out.header.len = sizeof(fuse_out_header) + | |||||
sizeof(attrs1); | |||||
}) | |||||
); | |||||
ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0)) | |||||
<< strerror(errno); | |||||
} | |||||
TEST_F(Listxattr, size_only_really_big) | TEST_F(Listxattr, size_only_really_big) | ||||
{ | { | ||||
uint64_t ino = 42; | uint64_t ino = 42; | ||||
int ns = EXTATTR_NAMESPACE_USER; | int ns = EXTATTR_NAMESPACE_USER; | ||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { | expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { | ||||
out.body.listxattr.size = 16000; | out.body.listxattr.size = 16000; | ||||
SET_OUT_HEADER_LEN(out, listxattr); | SET_OUT_HEADER_LEN(out, listxattr); | ||||
})); | })); | ||||
// TODO: fix the expected size after fixing the size calculation bug in | expect_listxattr(ino, 16000, | ||||
// fuse_vnop_listextattr. It should be exactly 16000. | |||||
expect_listxattr(ino, 16008, | |||||
ReturnImmediate([](auto in __unused, auto& out) { | ReturnImmediate([](auto in __unused, auto& out) { | ||||
const char l[16] = "user.foobarbang"; | const char l[16] = "user.foobarbang"; | ||||
for (int i=0; i < 1000; i++) { | for (int i=0; i < 1000; i++) { | ||||
memcpy(&out.body.bytes[16 * i], l, 16); | memcpy(&out.body.bytes[16 * i], l, 16); | ||||
} | } | ||||
out.header.len = sizeof(fuse_out_header) + 16000; | out.header.len = sizeof(fuse_out_header) + 16000; | ||||
}) | }) | ||||
); | ); | ||||
Show All 17 Lines | TEST_F(Listxattr, user) | ||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, | expect_listxattr(ino, 0, | ||||
ReturnImmediate([&](auto in __unused, auto& out) { | ReturnImmediate([&](auto in __unused, auto& out) { | ||||
out.body.listxattr.size = sizeof(attrs); | out.body.listxattr.size = sizeof(attrs); | ||||
SET_OUT_HEADER_LEN(out, listxattr); | SET_OUT_HEADER_LEN(out, listxattr); | ||||
}) | }) | ||||
); | ); | ||||
// TODO: fix the expected size after fixing the size calculation bug in | expect_listxattr(ino, sizeof(attrs), | ||||
// fuse_vnop_listextattr. | |||||
expect_listxattr(ino, sizeof(attrs) + 8, | |||||
ReturnImmediate([&](auto in __unused, auto& out) { | ReturnImmediate([&](auto in __unused, auto& out) { | ||||
memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); | memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); | ||||
out.header.len = sizeof(fuse_out_header) + sizeof(attrs); | out.header.len = sizeof(fuse_out_header) + sizeof(attrs); | ||||
})); | })); | ||||
ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), | ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), | ||||
extattr_list_file(FULLPATH, ns, data, sizeof(data))) | extattr_list_file(FULLPATH, ns, data, sizeof(data))) | ||||
<< strerror(errno); | << strerror(errno); | ||||
Show All 15 Lines | TEST_F(Listxattr, system) | ||||
expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); | ||||
expect_listxattr(ino, 0, | expect_listxattr(ino, 0, | ||||
ReturnImmediate([&](auto in __unused, auto& out) { | ReturnImmediate([&](auto in __unused, auto& out) { | ||||
out.body.listxattr.size = sizeof(attrs); | out.body.listxattr.size = sizeof(attrs); | ||||
SET_OUT_HEADER_LEN(out, listxattr); | SET_OUT_HEADER_LEN(out, listxattr); | ||||
}) | }) | ||||
); | ); | ||||
// TODO: fix the expected size after fixing the size calculation bug in | expect_listxattr(ino, sizeof(attrs), | ||||
// fuse_vnop_listextattr. | |||||
expect_listxattr(ino, sizeof(attrs) + 8, | |||||
ReturnImmediate([&](auto in __unused, auto& out) { | ReturnImmediate([&](auto in __unused, auto& out) { | ||||
memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); | memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); | ||||
out.header.len = sizeof(fuse_out_header) + sizeof(attrs); | out.header.len = sizeof(fuse_out_header) + sizeof(attrs); | ||||
})); | })); | ||||
ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), | ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), | ||||
extattr_list_file(FULLPATH, ns, data, sizeof(data))) | extattr_list_file(FULLPATH, ns, data, sizeof(data))) | ||||
<< strerror(errno); | << strerror(errno); | ||||
▲ Show 20 Lines • Show All 176 Lines • Show Last 20 Lines |