Page MenuHomeFreeBSD

D24777.id71575.diff
No OneTemporary

D24777.id71575.diff

Index: sys/fs/fuse/fuse_internal.c
===================================================================
--- sys/fs/fuse/fuse_internal.c
+++ sys/fs/fuse/fuse_internal.c
@@ -158,6 +158,7 @@
return 0;
}
+SDT_PROBE_DEFINE0(fusefs, , internal, access_vadmin);
/* Synchronously send a FUSE_ACCESS operation */
int
fuse_internal_access(struct vnode *vp,
@@ -213,7 +214,12 @@
if (!fsess_isimpl(mp, FUSE_ACCESS))
return 0;
- if ((mode & (VWRITE | VAPPEND | VADMIN)) != 0)
+ if (mode & VADMIN) {
+ // The FUSE protocol doesn't have an equivalent of VADMIN, so
+ // it's a bug if we ever reach this point with that bit set.
+ SDT_PROBE0(fusefs, , internal, access_vadmin);
+ }
+ if ((mode & (VWRITE | VAPPEND)) != 0)
mask |= W_OK;
if ((mode & VREAD) != 0)
mask |= R_OK;
Index: sys/fs/fuse/fuse_vnops.c
===================================================================
--- sys/fs/fuse/fuse_vnops.c
+++ sys/fs/fuse/fuse_vnops.c
@@ -235,6 +235,7 @@
{
struct mount *mp = vnode_mount(vp);
struct fuse_data *data = fuse_get_mpdata(mp);
+ int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
/*
* Kernel-invoked always succeeds.
@@ -248,12 +249,16 @@
*/
switch (ns) {
case EXTATTR_NAMESPACE_SYSTEM:
- if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
+ if (default_permissions) {
return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM));
}
/* FALLTHROUGH */
case EXTATTR_NAMESPACE_USER:
- return (fuse_internal_access(vp, accmode, td, cred));
+ if (default_permissions) {
+ return (fuse_internal_access(vp, accmode, td, cred));
+ } else {
+ return (0);
+ }
default:
return (EPERM);
}
@@ -985,6 +990,8 @@
int wantparent = flags & (LOCKPARENT | WANTPARENT);
int islastcn = flags & ISLASTCN;
struct mount *mp = vnode_mount(dvp);
+ struct fuse_data *data = fuse_get_mpdata(mp);
+ int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
int err = 0;
int lookup_err = 0;
@@ -1108,7 +1115,11 @@
if (lookup_err) {
/* Entry not found */
if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
- err = fuse_internal_access(dvp, VWRITE, td, cred);
+ if (default_permissions)
+ err = fuse_internal_access(dvp, VWRITE, td,
+ cred);
+ else
+ err = 0;
if (!err) {
/*
* Set the SAVENAME flag to hold onto the
@@ -1191,7 +1202,7 @@
&fvdat->entry_cache_timeout);
if ((nameiop == DELETE || nameiop == RENAME) &&
- islastcn)
+ islastcn && default_permissions)
{
struct vattr dvattr;
@@ -1828,7 +1839,9 @@
if (vfs_isrdonly(mp))
return EROFS;
- err = fuse_internal_access(vp, accmode, td, cred);
+ if (data->dataflags & FSESS_DEFAULT_PERMISSIONS) {
+ err = fuse_internal_access(vp, accmode, td, cred);
+ }
if (err)
return err;
else
Index: tests/sys/fs/fusefs/access.cc
===================================================================
--- tests/sys/fs/fusefs/access.cc
+++ tests/sys/fs/fusefs/access.cc
@@ -31,6 +31,9 @@
*/
extern "C" {
+#include <sys/types.h>
+#include <sys/extattr.h>
+
#include <fcntl.h>
#include <unistd.h>
}
@@ -42,10 +45,33 @@
class Access: public FuseTest {
public:
+virtual void SetUp() {
+ FuseTest::SetUp();
+ // Clear the default FUSE_ACCESS expectation
+ Mock::VerifyAndClearExpectations(m_mock);
+}
+
void expect_lookup(const char *relpath, uint64_t ino)
{
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
}
+
+/*
+ * Expect tha FUSE_ACCESS will never be called for the given inode, with any
+ * bits in the supplied access_mask set
+ */
+void expect_noaccess(uint64_t ino, mode_t access_mask)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS &&
+ in.header.nodeid == ino &&
+ in.body.access.mask & access_mask);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+}
+
};
class RofsAccess: public Access {
@@ -56,6 +82,69 @@
}
};
+/*
+ * Change the mode of a file.
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except possibly
+ * during the lookup phase.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, chmod)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const uint64_t ino = 42;
+ const mode_t newmode = 0644;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([](auto in) {
+ return (in.header.opcode == FUSE_SETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.mode = S_IFREG | newmode;
+ })));
+
+ EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
+}
+
+/*
+ * Create a new file
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, create)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ mode_t mode = S_IFREG | 0755;
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, R_OK | W_OK);
+ //expect_lookup(RELPATH, ino);
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillOnce(Invoke(ReturnErrno(ENOENT)));
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_CREATE);
+ }, Eq(true)),
+ _)
+ ).WillOnce(ReturnErrno(EPERM));
+
+ EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, mode));
+ EXPECT_EQ(EPERM, errno);
+}
+
/* The error case of FUSE_ACCESS. */
TEST_F(Access, eaccess)
{
@@ -105,6 +194,33 @@
ASSERT_EQ(EROFS, errno);
}
+
+/*
+ * Lookup an extended attribute
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except possibly
+ * during the lookup phase.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, Getxattr)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+ char data[80];
+ int ns = EXTATTR_NAMESPACE_USER;
+ ssize_t r;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_lookup(RELPATH, ino);
+ expect_noaccess(ino, 0);
+ expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR));
+
+ r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data));
+ ASSERT_EQ(-1, r);
+ ASSERT_EQ(ENOATTR, errno);
+}
+
/* The successful case of FUSE_ACCESS. */
TEST_F(Access, ok)
{
@@ -118,4 +234,71 @@
expect_access(ino, access_mode, 0);
ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno);
+}
+
+/*
+ * Unlink a file
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except for
+ * search permissions on the parent directory.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, unlink)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
+ expect_noaccess(ino, 0);
+ expect_lookup(RELPATH, ino);
+ expect_unlink(1, RELPATH, EPERM);
+
+ ASSERT_NE(0, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
+}
+
+/*
+ * Lookup an extended attribute
+ *
+ * There should never be a FUSE_ACCESS sent for this operation, except possibly
+ * during the lookup phase.
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=245689
+ */
+TEST_F(Access, unlink_sticky_directory)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ uint64_t ino = 42;
+
+ expect_access(FUSE_ROOT_ID, X_OK, 0);
+ expect_noaccess(FUSE_ROOT_ID, W_OK | R_OK);
+ expect_noaccess(ino, 0);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out)
+ {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
+ out.body.attr.attr.mode = S_IFDIR | 01777;
+ out.body.attr.attr.uid = 0;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_ACCESS &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).Times(0);
+ expect_lookup(RELPATH, ino);
+ expect_unlink(FUSE_ROOT_ID, RELPATH, EPERM);
+
+ ASSERT_EQ(-1, unlink(FULLPATH));
+ ASSERT_EQ(EPERM, errno);
}
Index: tests/sys/fs/fusefs/default_permissions.cc
===================================================================
--- tests/sys/fs/fusefs/default_permissions.cc
+++ tests/sys/fs/fusefs/default_permissions.cc
@@ -125,7 +125,7 @@
out.body.attr.attr.mode = mode;
out.body.attr.attr.size = 0;
out.body.attr.attr.uid = uid;
- out.body.attr.attr.uid = gid;
+ out.body.attr.attr.gid = gid;
out.body.attr.attr_valid = attr_valid;
})));
}
Index: tests/sys/fs/fusefs/lookup.cc
===================================================================
--- tests/sys/fs/fusefs/lookup.cc
+++ tests/sys/fs/fusefs/lookup.cc
@@ -39,7 +39,31 @@
using namespace testing;
-class Lookup: public FuseTest {};
+class Lookup: public FuseTest {
+public:
+/*
+ * Expect a getattr of the mountpoint that may or may not happen. It usually
+ * doesn't, but it can, for example when mac_bsdextended.ko is loaded and
+ * active. In any case, it's allowed
+ */
+void maybe_expect_getattr()
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == 1);
+ }, Eq(true)),
+ _)
+ ).Times(AtMost(1))
+ .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = 1;
+ out.body.attr.attr.mode = S_IFDIR | 0755;
+ out.body.attr.attr_valid = UINT64_MAX;
+ })));
+}
+};
+
class Lookup_7_8: public Lookup {
public:
virtual void SetUp() {
@@ -301,6 +325,7 @@
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
+ //maybe_expect_getattr();
EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
Index: tests/sys/fs/fusefs/notify.cc
===================================================================
--- tests/sys/fs/fusefs/notify.cc
+++ tests/sys/fs/fusefs/notify.cc
@@ -323,12 +323,13 @@
EXPECT_EQ(0, (intptr_t)thr0_value);
}
-TEST_F(Notify, inval_inode_with_clean_cache)
+/* Invalidate I/O in the pattern generated by cephfs */
+TEST_F(Notify, inval_inode_like_cephfs)
{
const static char FULLPATH[] = "mountpoint/foo";
const static char RELPATH[] = "foo";
- const char CONTENTS0[] = "abcdefgh";
- const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
+ const char CONTENTS0[] = "0123456789";
+ const char CONTENTS1[] = "0123456789Singapore";
struct inval_inode_args iia;
struct stat sb;
ino_t ino = 42;
@@ -343,6 +344,25 @@
expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
expect_open(ino, 0, 1);
+ expect_read(ino, 0, size0, size0, CONTENTS0);
+
+ /* Fill the data cache */
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+ ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
+
+ /* Evict the data cache */
+ iia.mock = m_mock;
+ iia.ino = ino;
+ iia.off = 0;
+ iia.len = 0;
+ ASSERT_EQ(0, pthread_create(&th0, NULL, inval_inode, &iia))
+ << strerror(errno);
+ pthread_join(th0, &thr0_value);
+ EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /* cache attributes were purged; this will trigger a new GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
@@ -356,13 +376,45 @@
out.body.attr.attr.size = size1;
out.body.attr.attr.uid = uid;
})));
- expect_read(ino, 0, size0, size0, CONTENTS0);
expect_read(ino, 0, size1, size1, CONTENTS1);
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(uid, sb.st_uid);
+ EXPECT_EQ(size1, sb.st_size);
+ /* This read should not be serviced by cache */
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
+ EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
+
+ leak(fd);
+}
+
+TEST_F(Notify, inval_inode_with_clean_cache)
+{
+ const static char FULLPATH[] = "mountpoint/foo";
+ const static char RELPATH[] = "foo";
+ const char CONTENTS0[] = "abcdefgh";
+ const char CONTENTS1[] = "ijklmnopqrstuvwxyz";
+ struct inval_inode_args iia;
+ struct stat sb;
+ ino_t ino = 42;
+ void *thr0_value;
+ Sequence seq;
+ uid_t uid = 12345;
+ pthread_t th0;
+ ssize_t size0 = sizeof(CONTENTS0);
+ ssize_t size1 = sizeof(CONTENTS1);
+ char buf[80];
+ int fd;
+
+ expect_lookup(FUSE_ROOT_ID, RELPATH, ino, size0, seq);
+ expect_open(ino, 0, 1);
+ expect_read(ino, 0, size0, size0, CONTENTS0);
+
/* Fill the data cache */
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
- ASSERT_EQ(size0, read(fd, buf, size0)) << strerror(errno);
+ ASSERT_EQ(size0, read(fd, buf, sizeof(buf))) << strerror(errno);
EXPECT_EQ(0, memcmp(buf, CONTENTS0, size0));
/* Evict the data cache */
@@ -376,13 +428,27 @@
EXPECT_EQ(0, (intptr_t)thr0_value);
/* cache attributes were purged; this will trigger a new GETATTR */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.size = size1;
+ out.body.attr.attr.uid = uid;
+ })));
+ expect_read(ino, 0, size1, size1, CONTENTS1);
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
EXPECT_EQ(uid, sb.st_uid);
EXPECT_EQ(size1, sb.st_size);
/* This read should not be serviced by cache */
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
- ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
+ ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
leak(fd);
@@ -441,7 +507,7 @@
EXPECT_EQ(0, (intptr_t)thr0_value);
/* This read should be serviced by cache */
- ASSERT_EQ(size1, read(fd, buf, size1)) << strerror(errno);
+ ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
EXPECT_EQ(0, memcmp(buf, CONTENTS1, size1));
leak(fd);
@@ -451,13 +517,16 @@
{
const static char FULLPATH[] = "mountpoint/foo";
const static char RELPATH[] = "foo";
- const char CONTENTS[] = "abcdefgh";
+ const char CONTENTS0[] = "abcdefgh";
+ const char CONTENTS1[] = "ijklmnop";
struct inval_inode_args iia;
ino_t ino = 42;
void *thr0_value;
Sequence seq;
pthread_t th0;
- ssize_t bufsize = sizeof(CONTENTS);
+ ssize_t size0 = sizeof(CONTENTS0);
+ ssize_t size1 = sizeof(CONTENTS1);
+ char buf[80];
int fd;
expect_lookup(FUSE_ROOT_ID, RELPATH, ino, 0, seq);
@@ -466,9 +535,9 @@
/* Fill the data cache */
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd);
- ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+ ASSERT_EQ(size0, write(fd, CONTENTS0, size0)) << strerror(errno);
- expect_write(ino, 0, bufsize, CONTENTS);
+ expect_write(ino, 0, size0, CONTENTS0);
/*
* The FUSE protocol does not require an fsync here, but FreeBSD's
* bufobj_invalbuf sends it anyway
@@ -484,6 +553,26 @@
<< strerror(errno);
pthread_join(th0, &thr0_value);
EXPECT_EQ(0, (intptr_t)thr0_value);
+
+ /*
+ * Reading again should trigger another FUSE_READ because the cache
+ * is empty.
+ */
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.mode = S_IFREG | 0644;
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.size = size1;
+ })));
+ expect_read(ino, 0, size1, size1, CONTENTS1);
+ ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
+ ASSERT_EQ(size1, read(fd, buf, sizeof(buf))) << strerror(errno);
leak(fd);
}
Index: tests/sys/fs/fusefs/read.cc
===================================================================
--- tests/sys/fs/fusefs/read.cc
+++ tests/sys/fs/fusefs/read.cc
@@ -828,7 +828,7 @@
/* sendfile should fail gracefully if fuse declines the read */
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
-TEST_F(Read, sendfile_eio)
+TEST_F(Read, DISABLED_sendfile_eio)
{
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
Index: tests/sys/fs/fusefs/rename.cc
===================================================================
--- tests/sys/fs/fusefs/rename.cc
+++ tests/sys/fs/fusefs/rename.cc
@@ -53,24 +53,6 @@
FuseTest::TearDown();
}
-
- void expect_getattr(uint64_t ino, mode_t mode)
- {
- EXPECT_CALL(*m_mock, process(
- ResultOf([=](auto in) {
- return (in.header.opcode == FUSE_GETATTR &&
- in.header.nodeid == ino);
- }, Eq(true)),
- _)
- ).WillOnce(Invoke(
- ReturnImmediate([=](auto i __unused, auto& out) {
- SET_OUT_HEADER_LEN(out, attr);
- out.body.attr.attr.ino = ino; // Must match nodeid
- out.body.attr.attr.mode = mode;
- out.body.attr.attr_valid = UINT64_MAX;
- })));
- }
-
};
// EINVAL, dst is subdir of src
@@ -82,7 +64,6 @@
const char RELSRC[] = "src";
uint64_t src_ino = 42;
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2);
EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
@@ -123,7 +104,6 @@
*/
struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
/* LOOKUP returns a negative cache entry for dst */
EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
@@ -158,7 +138,6 @@
uint64_t ino = 42;
struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
/* LOOKUP returns a negative cache entry for dst */
EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
@@ -196,7 +175,6 @@
tmpfd = mkstemp(tmpfile);
ASSERT_LE(0, tmpfd) << strerror(errno);
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2);
ASSERT_NE(0, rename(tmpfile, FULLB));
@@ -215,7 +193,6 @@
uint64_t dst_dir_ino = FUSE_ROOT_ID;
uint64_t ino = 42;
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
EXPECT_LOOKUP(FUSE_ROOT_ID, RELDST)
.WillOnce(Invoke(ReturnErrno(ENOENT)));
@@ -251,7 +228,6 @@
struct stat sb;
expect_lookup(RELSRC, ino, S_IFDIR | 0755, 0, 1);
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
EXPECT_LOOKUP(FUSE_ROOT_ID, RELDSTDIR)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
SET_OUT_HEADER_LEN(out, entry);
@@ -303,7 +279,6 @@
uint64_t dst_dir_ino = FUSE_ROOT_ID;
uint64_t ino = 42;
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1);
expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1);
EXPECT_CALL(*m_mock, process(
Index: tests/sys/fs/fusefs/rmdir.cc
===================================================================
--- tests/sys/fs/fusefs/rmdir.cc
+++ tests/sys/fs/fusefs/rmdir.cc
@@ -42,22 +42,6 @@
class Rmdir: public FuseTest {
public:
-void expect_getattr(uint64_t ino, mode_t mode)
-{
- EXPECT_CALL(*m_mock, process(
- ResultOf([=](auto in) {
- return (in.header.opcode == FUSE_GETATTR &&
- in.header.nodeid == ino);
- }, Eq(true)),
- _)
- ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
- SET_OUT_HEADER_LEN(out, attr);
- out.body.attr.attr.ino = ino; // Must match nodeid
- out.body.attr.attr.mode = mode;
- out.body.attr.attr_valid = UINT64_MAX;
- })));
-}
-
void expect_lookup(const char *relpath, uint64_t ino, int times=1)
{
EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
@@ -95,25 +79,34 @@
struct stat sb;
sem_t sem;
uint64_t ino = 42;
+ Sequence seq;
ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
+ expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_RMDIR &&
+ 0 == strcmp(RELPATH, in.body.rmdir) &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(0)));
+ expect_forget(ino, 1, &sem);
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == FUSE_ROOT_ID);
}, Eq(true)),
_)
- ).Times(2)
+ ).InSequence(seq)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
- out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
out.body.attr.attr.mode = S_IFDIR | 0755;
out.body.attr.attr_valid = UINT64_MAX;
})));
- expect_lookup(RELPATH, ino);
- expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
- expect_forget(ino, 1, &sem);
ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno);
EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
@@ -127,7 +120,6 @@
const char RELPATH[] = "some_dir";
uint64_t ino = 42;
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELPATH, ino);
expect_rmdir(FUSE_ROOT_ID, RELPATH, ENOTEMPTY);
@@ -143,7 +135,6 @@
sem_t sem;
uint64_t ino = 42;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH, ino, 2);
expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
expect_forget(ino, 1, &sem);
@@ -163,7 +154,6 @@
ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
- expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755);
expect_lookup(RELPATH, ino);
expect_rmdir(FUSE_ROOT_ID, RELPATH, 0);
expect_forget(ino, 1, &sem);
Index: tests/sys/fs/fusefs/unlink.cc
===================================================================
--- tests/sys/fs/fusefs/unlink.cc
+++ tests/sys/fs/fusefs/unlink.cc
@@ -41,22 +41,6 @@
class Unlink: public FuseTest {
public:
-void expect_getattr(uint64_t ino, mode_t mode)
-{
- EXPECT_CALL(*m_mock, process(
- ResultOf([=](auto in) {
- return (in.header.opcode == FUSE_GETATTR &&
- in.header.nodeid == ino);
- }, Eq(true)),
- _)
- ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
- SET_OUT_HEADER_LEN(out, attr);
- out.body.attr.attr.ino = ino; // Must match nodeid
- out.body.attr.attr.mode = mode;
- out.body.attr.attr_valid = UINT64_MAX;
- })));
-}
-
void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1)
{
EXPECT_LOOKUP(FUSE_ROOT_ID, relpath)
@@ -89,7 +73,6 @@
struct stat sb_old, sb_new;
int fd1;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH0, ino, 1, 2);
expect_lookup(RELPATH1, ino, 1, 2);
expect_open(ino, 0, 1);
@@ -117,23 +100,32 @@
const char RELPATH[] = "some_file.txt";
struct stat sb;
uint64_t ino = 42;
+ Sequence seq;
+ /* Use nlink=2 so we don't get a FUSE_FORGET */
+ expect_lookup(RELPATH, ino, 1, 2);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_UNLINK &&
+ 0 == strcmp(RELPATH, in.body.unlink) &&
+ in.header.nodeid == FUSE_ROOT_ID);
+ }, Eq(true)),
+ _)
+ ).InSequence(seq)
+ .WillOnce(Invoke(ReturnErrno(0)));
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
return (in.header.opcode == FUSE_GETATTR &&
in.header.nodeid == FUSE_ROOT_ID);
}, Eq(true)),
_)
- ).Times(2)
+ ).InSequence(seq)
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
SET_OUT_HEADER_LEN(out, attr);
- out.body.attr.attr.ino = ino; // Must match nodeid
+ out.body.attr.attr.ino = FUSE_ROOT_ID;
out.body.attr.attr.mode = S_IFDIR | 0755;
out.body.attr.attr_valid = UINT64_MAX;
})));
- /* Use nlink=2 so we don't get a FUSE_FORGET */
- expect_lookup(RELPATH, ino, 1, 2);
- expect_unlink(1, RELPATH, 0);
ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno);
EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
@@ -145,7 +137,6 @@
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH, ino, 1);
expect_unlink(1, RELPATH, EPERM);
@@ -162,7 +153,6 @@
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH, ino, 2, 2);
expect_unlink(1, RELPATH, 0);
@@ -182,7 +172,6 @@
const char RELPATH1[] = "other_file.txt";
uint64_t ino = 42;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH0, ino, 1, 2);
expect_unlink(1, RELPATH0, 0);
EXPECT_CALL(*m_mock, process(
@@ -213,7 +202,6 @@
ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH, ino, 1);
expect_unlink(1, RELPATH, 0);
expect_forget(ino, 1, &sem);
@@ -233,7 +221,6 @@
uint64_t ino = 42;
int fd;
- expect_getattr(1, S_IFDIR | 0755);
expect_lookup(RELPATH0, ino, 2);
expect_open(ino, 0, 1);
expect_unlink(1, RELPATH0, 0);
Index: tests/sys/fs/fusefs/utils.hh
===================================================================
--- tests/sys/fs/fusefs/utils.hh
+++ tests/sys/fs/fusefs/utils.hh
@@ -133,6 +133,12 @@
void expect_getattr(uint64_t ino, uint64_t size);
/*
+ * Create an expectation that FUSE_GETXATTR will be called once for the
+ * given inode.
+ */
+ void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r);
+
+ /*
* Create an expectation that FUSE_LOOKUP will be called for the given
* path exactly times times and cache validity period. It will respond
* with inode ino, mode mode, filesize size.
Index: tests/sys/fs/fusefs/utils.cc
===================================================================
--- tests/sys/fs/fusefs/utils.cc
+++ tests/sys/fs/fusefs/utils.cc
@@ -273,6 +273,20 @@
})));
}
+void FuseTest::expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r)
+{
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ const char *a = (const char*)in.body.bytes +
+ sizeof(fuse_getxattr_in);
+ return (in.header.opcode == FUSE_GETXATTR &&
+ in.header.nodeid == ino &&
+ 0 == strcmp(attr, a));
+ }, Eq(true)),
+ _)
+ ).WillOnce(Invoke(r));
+}
+
void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
{
Index: tests/sys/fs/fusefs/xattr.cc
===================================================================
--- tests/sys/fs/fusefs/xattr.cc
+++ tests/sys/fs/fusefs/xattr.cc
@@ -62,20 +62,6 @@
class Xattr: public FuseTest {
public:
-void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r)
-{
- EXPECT_CALL(*m_mock, process(
- ResultOf([=](auto in) {
- const char *a = (const char*)in.body.bytes +
- sizeof(fuse_getxattr_in);
- return (in.header.opcode == FUSE_GETXATTR &&
- in.header.nodeid == ino &&
- 0 == strcmp(attr, a));
- }, Eq(true)),
- _)
- ).WillOnce(Invoke(r));
-}
-
void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r,
Sequence *seq = NULL)
{

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 13, 9:29 AM (17 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17133385
Default Alt Text
D24777.id71575.diff (26 KB)

Event Timeline