Index: projects/fuse2/etc/mtree/BSD.tests.dist =================================================================== --- projects/fuse2/etc/mtree/BSD.tests.dist (revision 345355) +++ projects/fuse2/etc/mtree/BSD.tests.dist (revision 345356) @@ -1,1074 +1,1074 @@ # $FreeBSD$ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin cat .. chflags .. chmod .. date .. dd .. echo .. expr .. ln .. ls .. mkdir .. mv .. pax .. pkill .. pwait .. rm .. rmdir .. sh builtins .. errors .. execution .. expansion .. invocation .. parameters .. parser .. set-e .. .. sleep .. test .. .. cddl lib .. sbin .. usr.bin ctfconvert .. ztest .. .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. env .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. sugar .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. .. zfsd .. .. .. etc rc.d .. .. games .. gnu lib .. usr.bin diff .. .. .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. csu dynamic .. dynamiclib .. static .. .. googletest gmock .. gmock_main .. gtest .. gtest_main .. .. libarchive .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcam .. libcasper services cap_dns .. cap_grp .. cap_pwd .. cap_sysctl .. .. .. libcrypt .. libdevdctl .. libkvm .. libmp .. libnv .. libproc .. libregex data .. .. librt .. libsbuf .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-sh .. .. rtld-elf .. tftpd .. .. sbin bectl .. dhclient .. devd .. growfs .. ifconfig .. mdconfig .. pfctl files .. .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. googletest .. plain .. tap .. .. .. zoneinfo .. .. sys acl .. aio .. audit .. auditpipe .. capsicum .. cddl zfs bin .. include .. tests acl cifs .. nontrivial .. trivial .. .. atime .. bootfs .. cache .. cachefile .. clean_mirror .. cli_root zfs_upgrade .. zfs_promote .. zfs_clone .. zfs_property .. zfs_destroy .. zpool_create .. zpool_history .. zpool_expand .. zpool_remove .. zfs_mount .. zfs_unshare .. zdb .. zpool_online .. zpool_get .. zpool_export .. zfs_copies .. zfs_get .. zfs .. zpool_clear .. zpool_import blockfiles .. .. zpool .. zpool_offline .. zpool_replace .. zfs_rollback .. zpool_set .. zfs_send .. zfs_set .. zpool_detach .. zfs_diff .. zpool_scrub .. zfs_inherit .. zfs_snapshot .. zfs_share .. zpool_destroy .. zpool_status .. zfs_unmount .. zfs_receive .. zfs_create .. zpool_upgrade blockfiles .. .. zpool_add .. zfs_rename .. zpool_attach .. zfs_reservation .. .. cli_user misc .. zfs_list .. zpool_iostat .. zpool_list .. .. compression .. ctime .. delegate .. devices .. exec .. grow_pool .. grow_replicas .. history .. hotplug .. hotspare .. inheritance .. interop .. inuse .. iscsi .. large_files .. largest_pool .. link_count .. migration .. mmap .. mount .. mv_files .. nestedfs .. no_space .. online_offline .. pool_names .. poolversion .. quota .. redundancy .. refquota .. refreserv .. rename_dirs .. replacement .. reservation .. rootpool .. rsend .. scrub_mirror .. slog .. snapshot .. snapused .. sparse .. threadsappend .. truncate .. txg_integrity .. userquota .. utils_test .. write_dirs .. xattr .. zfsd .. zil .. zinject .. zones .. zvol zvol_ENOSPC .. zvol_cli .. zvol_misc .. zvol_swap .. .. zvol_thrash .. .. .. .. dtrace .. fifo .. file .. fs - fuse + fusefs .. tmpfs .. .. geom class concat .. eli .. gate .. gpt .. mirror .. nop .. part .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue libkqueue .. .. mac bsdextended .. portacl .. .. mqueue .. net .. netinet .. netipsec tunnel .. .. netmap .. netpfil pf ioctl .. .. .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. utimensat .. .. posixshm .. sys .. vfs .. vm .. .. usr.bin apply .. awk .. basename .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. compress .. cpio .. col .. comm .. csplit .. cut .. dc .. diff .. dirname .. du .. file2c .. find .. fold .. getconf .. grep .. gzip .. head .. hexdump .. ident .. indent .. join .. jot .. lastcomm .. limits .. m4 .. mkimg .. ncal .. opensm .. pr .. printf .. procstat .. rs .. sdiff .. sed regress.multitest.out .. .. seq .. soelim .. stat .. tail .. tar .. timeout .. tr .. truncate .. units .. uudecode .. uuencode .. uniq .. vmstat .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin chown .. etcupdate .. extattr .. fstyp .. makefs .. newsyslog .. nmtree .. praudit .. pw .. rpcbind .. sa .. .. .. # vim: set expandtab ts=4 sw=4: Index: projects/fuse2/tests/sys/fs/fuse/setattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/setattr.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/setattr.cc (nonexistent) @@ -1,512 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include - -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Setattr : public FuseTest {}; - - -/* - * If setattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs - * should use the cached attributes, rather than query the daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ -TEST_F(Setattr, DISABLED_attr_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - struct stat sb; - const mode_t newmode = 0644; - - EXPECT_LOOKUP(1, 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; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - /* In protocol 7.23, ctime will be changed too */ - 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_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_GETATTR); - }, Eq(true)), - _) - ).Times(0); - - /* Set an attribute with SETATTR */ - ASSERT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno); - - /* The stat(2) should use cached attributes */ - ASSERT_EQ(0, stat(FULLPATH, &sb)); - EXPECT_EQ(S_IFREG | newmode, sb.st_mode); -} - -/* Change the mode of a file */ -TEST_F(Setattr, chmod) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const mode_t oldmode = 0755; - const mode_t newmode = 0644; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFREG | oldmode; - out->body.entry.nodeid = ino; - out->body.entry.attr.mode = S_IFREG | oldmode; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_MODE; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.mode == newmode); - }, 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); -} - -/* Change the owner and group of a file */ -TEST_F(Setattr, chown) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const gid_t oldgroup = 66; - const gid_t newgroup = 99; - const uid_t olduser = 33; - const uid_t newuser = 44; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(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.gid = oldgroup; - out->body.entry.attr.uid = olduser; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_GID | FATTR_UID; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.uid == newuser && - in->body.setattr.gid == newgroup); - }, 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 | 0644; - out->body.attr.attr.uid = newuser; - out->body.attr.attr.gid = newgroup; - }))); - EXPECT_EQ(0, chown(FULLPATH, newuser, newgroup)) << strerror(errno); -} - - - -/* - * FUSE daemons are allowed to check permissions however they like. If the - * daemon returns EPERM, even if the file permissions "should" grant access, - * then fuse(4) should return EPERM too. - */ -TEST_F(Setattr, eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFREG | 0777; - out->body.entry.nodeid = ino; - out->body.entry.attr.uid = in->header.uid; - out->body.entry.attr.gid = in->header.gid; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EPERM))); - EXPECT_NE(0, truncate(FULLPATH, 10)); - EXPECT_EQ(EPERM, errno); -} - -/* Change the mode of an open file, by its file descriptor */ -TEST_F(Setattr, fchmod) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - const mode_t oldmode = 0755; - const mode_t newmode = 0644; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFREG | oldmode; - out->body.entry.nodeid = ino; - out->body.entry.attr_valid = UINT64_MAX; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | oldmode; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_MODE; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.mode == newmode); - }, 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; - }))); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Change the size of an open file, by its file descriptor */ -TEST_F(Setattr, ftruncate) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - uint64_t fh = 0xdeadbeef1a7ebabe; - const off_t oldsize = 99; - const off_t newsize = 12345; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFREG | 0755; - out->body.entry.nodeid = ino; - out->body.entry.attr_valid = UINT64_MAX; - out->body.entry.attr.size = oldsize; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - out->body.open.fh = fh; - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0755; - out->body.attr.attr.size = oldsize; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_SIZE | FATTR_FH; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.fh == fh); - }, 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 | 0755; - out->body.attr.attr.size = newsize; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, ftruncate(fd, newsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Change the size of the file */ -TEST_F(Setattr, truncate) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const uint64_t oldsize = 100'000'000; - const uint64_t newsize = 20'000'000; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(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.size = oldsize; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_SIZE; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.size == newsize); - }, 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 | 0644; - out->body.attr.attr.size = newsize; - }))); - EXPECT_EQ(0, truncate(FULLPATH, newsize)) << strerror(errno); -} - -/* Change a file's timestamps */ -TEST_F(Setattr, utimensat) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const timespec oldtimes[2] = { - {.tv_sec = 1, .tv_nsec = 2}, - {.tv_sec = 3, .tv_nsec = 4}, - }; - const timespec newtimes[2] = { - {.tv_sec = 5, .tv_nsec = 6}, - {.tv_sec = 7, .tv_nsec = 8}, - }; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(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_valid = UINT64_MAX; - out->body.entry.attr.atime = oldtimes[0].tv_sec; - out->body.entry.attr.atimensec = oldtimes[0].tv_nsec; - out->body.entry.attr.mtime = oldtimes[1].tv_sec; - out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec; - }))); - - /* - * Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR - * call - */ - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_GETATTR && - 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 | 0644; - out->body.attr.attr.atime = oldtimes[0].tv_sec; - out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; - out->body.attr.attr.mtime = oldtimes[1].tv_sec; - out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_ATIME | FATTR_MTIME; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.atime == newtimes[0].tv_sec && - in->body.setattr.atimensec == - newtimes[0].tv_nsec && - in->body.setattr.mtime == newtimes[1].tv_sec && - in->body.setattr.mtimensec == - newtimes[1].tv_nsec); - }, 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 | 0644; - out->body.attr.attr.atime = newtimes[0].tv_sec; - out->body.attr.attr.atimensec = newtimes[0].tv_nsec; - out->body.attr.attr.mtime = newtimes[1].tv_sec; - out->body.attr.attr.mtimensec = newtimes[1].tv_nsec; - }))); - EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0)) - << strerror(errno); -} - -/* Change a file mtime but not its atime */ -TEST_F(Setattr, utimensat_mtime_only) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const timespec oldtimes[2] = { - {.tv_sec = 1, .tv_nsec = 2}, - {.tv_sec = 3, .tv_nsec = 4}, - }; - const timespec newtimes[2] = { - {.tv_sec = 5, .tv_nsec = UTIME_OMIT}, - {.tv_sec = 7, .tv_nsec = 8}, - }; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(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_valid = UINT64_MAX; - out->body.entry.attr.atime = oldtimes[0].tv_sec; - out->body.entry.attr.atimensec = oldtimes[0].tv_nsec; - out->body.entry.attr.mtime = oldtimes[1].tv_sec; - out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec; - }))); - - /* - * Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR - * call - */ - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_GETATTR && - 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 | 0644; - out->body.attr.attr.atime = oldtimes[0].tv_sec; - out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; - out->body.attr.attr.mtime = oldtimes[1].tv_sec; - out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - /* In protocol 7.23, ctime will be changed too */ - uint32_t valid = FATTR_MTIME; - return (in->header.opcode == FUSE_SETATTR && - in->header.nodeid == ino && - in->body.setattr.valid == valid && - in->body.setattr.mtime == newtimes[1].tv_sec && - in->body.setattr.mtimensec == - newtimes[1].tv_nsec); - }, 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 | 0644; - out->body.attr.attr.atime = oldtimes[0].tv_sec; - out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; - out->body.attr.attr.mtime = newtimes[1].tv_sec; - out->body.attr.attr.mtimensec = newtimes[1].tv_nsec; - }))); - EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0)) - << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/setattr.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/locks.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/locks.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/locks.cc (nonexistent) @@ -1,426 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -/* This flag value should probably be defined in fuse_kernel.h */ -#define OFFSET_MAX 0x7fffffffffffffffLL - -using namespace testing; - -/* For testing filesystems without posix locking support */ -class Fallback: public FuseTest { -public: - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} - -}; - -/* For testing filesystems with posix locking support */ -class Locks: public Fallback { - virtual void SetUp() { - m_init_flags = FUSE_POSIX_LOCKS; - Fallback::SetUp(); - } -}; - -class GetlkFallback: public Fallback {}; -class Getlk: public Locks {}; -class SetlkFallback: public Fallback {}; -class Setlk: public Locks {}; -class SetlkwFallback: public Fallback {}; -class Setlkw: public Locks {}; - -/* - * If the fuse filesystem does not support posix file locks, then the kernel - * should fall back to local locks. - */ -TEST_F(GetlkFallback, local) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = getpid(); - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the filesystem has no locks that fit the description, the filesystem - * should return F_UNLCK - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Getlk, DISABLED_no_locks) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETLK && - in->header.nodeid == ino && - in->body.getlk.fh == FH && - in->body.getlk.owner == (uint32_t)pid && - in->body.getlk.lk.start == 10 && - in->body.getlk.lk.end == 1009 && - in->body.getlk.lk.type == F_RDLCK && - in->body.getlk.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, getlk); - out->body.getlk.lk = in->body.getlk.lk; - out->body.getlk.lk.type = F_UNLCK; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); - ASSERT_EQ(F_UNLCK, fl.l_type); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* A different pid does have a lock */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Getlk, DISABLED_lock_exists) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - pid_t pid2 = 1235; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETLK && - in->header.nodeid == ino && - in->body.getlk.fh == FH && - in->body.getlk.owner == (uint32_t)pid && - in->body.getlk.lk.start == 10 && - in->body.getlk.lk.end == 1009 && - in->body.getlk.lk.type == F_RDLCK && - in->body.getlk.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, getlk); - out->body.getlk.lk.start = 100; - out->body.getlk.lk.end = 199; - out->body.getlk.lk.type = F_WRLCK; - out->body.getlk.lk.pid = (uint32_t)pid2;; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); - EXPECT_EQ(100, fl.l_start); - EXPECT_EQ(100, fl.l_len); - EXPECT_EQ(pid2, fl.l_pid); - EXPECT_EQ(F_WRLCK, fl.l_type); - EXPECT_EQ(SEEK_SET, fl.l_whence); - EXPECT_EQ(0, fl.l_sysid); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the fuse filesystem does not support posix file locks, then the kernel - * should fall back to local locks. - */ -TEST_F(SetlkFallback, local) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = getpid(); - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Set a new lock with FUSE_SETLK */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Setlk, DISABLED_set) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlk.fh == FH && - in->body.setlk.owner == (uint32_t)pid && - in->body.setlk.lk.start == 10 && - in->body.setlk.lk.end == 1009 && - in->body.setlk.lk.type == F_RDLCK && - in->body.setlk.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, setlk); - out->body.setlk.lk = in->body.setlk.lk; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* l_len = 0 is a flag value that means to lock until EOF */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Setlk, DISABLED_set_eof) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlk.fh == FH && - in->body.setlk.owner == (uint32_t)pid && - in->body.setlk.lk.start == 10 && - in->body.setlk.lk.end == OFFSET_MAX && - in->body.setlk.lk.type == F_RDLCK && - in->body.setlk.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, setlk); - out->body.setlk.lk = in->body.setlk.lk; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 0; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Fail to set a new lock with FUSE_SETLK due to a conflict */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Setlk, DISABLED_eagain) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlk.fh == FH && - in->body.setlk.owner == (uint32_t)pid && - in->body.setlk.lk.start == 10 && - in->body.setlk.lk.end == 1009 && - in->body.setlk.lk.type == F_RDLCK && - in->body.setlk.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EAGAIN))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl)); - ASSERT_EQ(EAGAIN, errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the fuse filesystem does not support posix file locks, then the kernel - * should fall back to local locks. - */ -TEST_F(SetlkwFallback, local) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = getpid(); - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Set a new lock with FUSE_SETLK. If the lock is not available, then the - * command should block. But to the kernel, that's the same as just being - * slow, so we don't need a separate test method - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(Setlkw, DISABLED_set) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct flock fl; - int fd; - pid_t pid = 1234; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlkw.fh == FH && - in->body.setlkw.owner == (uint32_t)pid && - in->body.setlkw.lk.start == 10 && - in->body.setlkw.lk.end == 1009 && - in->body.setlkw.lk.type == F_RDLCK && - in->body.setlkw.lk.pid == (uint64_t)pid); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, setlkw); - out->body.setlkw.lk = in->body.setlkw.lk; - }))); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 10; - fl.l_len = 1000; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/locks.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/write.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/write.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/write.cc (nonexistent) @@ -1,659 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -#include -#include - -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Write: public FuseTest { - -public: - -void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); -} - -void expect_release(uint64_t ino, ProcessMockerT r) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_RELEASE && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(r)); -} - -void require_sync_resize_0() { - const char *sync_resize_node = "vfs.fuse.sync_resize"; - int val = 0; - size_t size = sizeof(val); - - ASSERT_EQ(0, sysctlbyname(sync_resize_node, &val, &size, NULL, 0)) - << strerror(errno); - if (val != 0) - GTEST_SKIP() << - "vfs.fuse.sync_resize must be set to 0 for this test." - " That sysctl will probably be removed soon."; -} - -}; - -class AioWrite: public Write { -virtual void SetUp() { - const char *node = "vfs.aio.enable_unsafe"; - int val = 0; - size_t size = sizeof(val); - - FuseTest::SetUp(); - - ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) - << strerror(errno); - if (!val) - GTEST_SKIP() << - "vfs.aio.enable_unsafe must be set for this test"; -} -}; - -/* Tests for the write-through cache mode */ -class WriteThrough: public Write { - -virtual void SetUp() { - const char *cache_mode_node = "vfs.fuse.data_cache_mode"; - int val = 0; - size_t size = sizeof(val); - - FuseTest::SetUp(); - if (IsSkipped()) - return; - - ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0)) - << strerror(errno); - if (val != 1) - GTEST_SKIP() << "vfs.fuse.data_cache_mode must be set to 1 " - "(writethrough) for this test"; -} - -}; - -/* Tests for the writeback cache mode */ -class WriteBack: public Write { - -virtual void SetUp() { - const char *node = "vfs.fuse.data_cache_mode"; - int val = 0; - size_t size = sizeof(val); - - FuseTest::SetUp(); - if (IsSkipped()) - return; - - ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) - << strerror(errno); - if (val != 2) - GTEST_SKIP() << "vfs.fuse.data_cache_mode must be set to 2 " - "(writeback) for this test"; -} - -}; - -/* AIO writes need to set the header's pid field correctly */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ -TEST_F(AioWrite, DISABLED_aio_write) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - uint64_t offset = 4096; - int fd; - ssize_t bufsize = strlen(CONTENTS); - struct aiocb iocb, *piocb; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - iocb.aio_nbytes = bufsize; - iocb.aio_fildes = fd; - iocb.aio_buf = (void *)CONTENTS; - iocb.aio_offset = offset; - iocb.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno); - ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * When a file is opened with O_APPEND, we should forward that flag to - * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the - * offset internally. That way we'll work both with filesystems that - * understand O_APPEND (and ignore the offset) and filesystems that don't (and - * simply use the offset). - * - * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the - * Open.o_append test. - */ -TEST_F(Write, append) -{ - const ssize_t BUFSIZE = 9; - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char CONTENTS[BUFSIZE] = "abcdefgh"; - uint64_t ino = 42; - /* - * Set offset to a maxbcachebuf boundary so we don't need to RMW when - * using writeback caching - */ - uint64_t initial_offset = m_maxbcachebuf; - int fd; - - require_sync_resize_0(); - - expect_lookup(RELPATH, ino, initial_offset); - expect_open(ino, 0, 1); - expect_getattr(ino, initial_offset); - expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS); - - /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ - fd = open(FULLPATH, O_RDWR | O_APPEND); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -TEST_F(Write, append_direct_io) -{ - const ssize_t BUFSIZE = 9; - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char CONTENTS[BUFSIZE] = "abcdefgh"; - uint64_t ino = 42; - uint64_t initial_offset = 4096; - int fd; - - expect_lookup(RELPATH, ino, initial_offset); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, initial_offset); - expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS); - - fd = open(FULLPATH, O_WRONLY | O_APPEND); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* A direct write should evict any overlapping cached data */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235774 */ -TEST_F(Write, DISABLED_direct_io_evicts_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char CONTENTS0[] = "abcdefgh"; - const char CONTENTS1[] = "ijklmnop"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS0) + 1; - char readbuf[bufsize]; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - // Prime cache - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - - // Write directly, evicting cache - ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); - - // Read again. Cache should be bypassed - expect_read(ino, 0, bufsize, bufsize, CONTENTS1); - ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - ASSERT_STREQ(readbuf, CONTENTS1); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * When the direct_io option is used, filesystems are allowed to write less - * data than requested - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */ -TEST_F(Write, DISABLED_direct_io_short_write) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefghijklmnop"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - ssize_t halfbufsize = bufsize / 2; - const char *halfcontents = CONTENTS + halfbufsize; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS); - expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0, - halfcontents); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * An insidious edge case: the filesystem returns a short write, and the - * difference between what we requested and what it actually wrote crosses an - * iov element boundary - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */ -TEST_F(Write, DISABLED_direct_io_short_write_iov) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS0 = "abcdefgh"; - const char *CONTENTS1 = "ijklmnop"; - const char *EXPECTED0 = "abcdefghijklmnop"; - const char *EXPECTED1 = "hijklmnop"; - uint64_t ino = 42; - int fd; - ssize_t size0 = strlen(CONTENTS0) - 1; - ssize_t size1 = strlen(CONTENTS1) + 1; - ssize_t totalsize = size0 + size1; - struct iovec iov[2]; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, totalsize, size0, 0, EXPECTED0); - expect_write(ino, size0, size1, size1, 0, EXPECTED1); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - iov[0].iov_base = (void*)CONTENTS0; - iov[0].iov_len = strlen(CONTENTS0); - iov[1].iov_base = (void*)CONTENTS1; - iov[1].iov_len = strlen(CONTENTS1); - ASSERT_EQ(totalsize, writev(fd, iov, 2)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the kernel cannot be sure which uid, gid, or pid was responsible for a - * write, then it must set the FUSE_WRITE_CACHE bit - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ -// TODO: check vfs.fuse.mmap_enable -TEST_F(Write, DISABLED_mmap) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - void *p; - uint64_t offset = 10; - size_t len; - void *zeros, *expected; - - len = getpagesize(); - - zeros = calloc(1, len); - ASSERT_NE(NULL, zeros); - expected = calloc(1, len); - ASSERT_NE(NULL, expected); - memmove((uint8_t*)expected + offset, CONTENTS, bufsize); - - expect_lookup(RELPATH, ino, len); - expect_open(ino, 0, 1); - expect_getattr(ino, len); - expect_read(ino, 0, len, len, zeros); - /* - * Writes from the pager may or may not be associated with the correct - * pid, so they must set FUSE_WRITE_CACHE - */ - expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, expected); - expect_release(ino, ReturnErrno(0)); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - ASSERT_NE(MAP_FAILED, p) << strerror(errno); - - memmove((uint8_t*)p + offset, CONTENTS, bufsize); - - ASSERT_EQ(0, munmap(p, len)) << strerror(errno); - close(fd); // Write mmap'd data on close - - free(expected); - free(zeros); -} - -TEST_F(Write, pwrite) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - uint64_t offset = 4096; - int fd; - ssize_t bufsize = strlen(CONTENTS); - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) - << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -TEST_F(Write, write) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* fuse(4) should not issue writes of greater size than the daemon requests */ -TEST_F(Write, write_large) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - int *contents; - uint64_t ino = 42; - int fd; - ssize_t halfbufsize, bufsize; - - halfbufsize = m_mock->m_max_write; - bufsize = halfbufsize * 2; - contents = (int*)malloc(bufsize); - ASSERT_NE(NULL, contents); - for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { - contents[i] = i; - } - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents); - expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0, - &contents[halfbufsize / sizeof(int)]); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ - - free(contents); -} - -TEST_F(Write, write_nothing) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = ""; - uint64_t ino = 42; - int fd; - ssize_t bufsize = 0; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* In writeback mode, dirty data should be written on close */ -TEST_F(WriteBack, close) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETATTR); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, attr); - out->body.attr.attr.ino = ino; // Must match nodeid - }))); - expect_release(ino, ReturnErrno(0)); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - close(fd); -} - -/* - * Without direct_io, writes should be committed to cache - */ -TEST_F(WriteBack, writeback) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char readbuf[bufsize]; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* - * A subsequent read should be serviced by cache, without querying the - * filesystem daemon - */ - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * With O_DIRECT, writes should be not committed to cache. Admittedly this is - * an odd test, because it would be unusual to use O_DIRECT for writes but not - * reads. - */ -TEST_F(WriteBack, o_direct) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char readbuf[bufsize]; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDWR | O_DIRECT); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* A subsequent read must query the daemon because cache is empty */ - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Without direct_io, writes should be committed to cache - */ -/* - * Disabled because we don't yet implement write-through caching. No bugzilla - * entry, because that's a feature request, not a bug. - */ -TEST_F(WriteThrough, DISABLED_writethrough) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char readbuf[bufsize]; - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* - * A subsequent read should be serviced by cache, without querying the - * filesystem daemon - */ - ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* With writethrough caching, writes update the cached file size */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ -TEST_F(WriteThrough, DISABLED_update_file_size) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - struct stat sb; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - - expect_lookup(RELPATH, ino, 0); - expect_open(ino, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).Times(2) - .WillRepeatedly(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 | 0644; - out->body.attr.attr.size = 0; - out->body.attr.attr_valid = UINT64_MAX; - }))); - expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); - - fd = open(FULLPATH, O_RDWR); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - /* Get cached attributes */ - ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); - ASSERT_EQ(bufsize, sb.st_size); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/write.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/mkdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/mkdir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/mkdir.cc (nonexistent) @@ -1,178 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Mkdir: public FuseTest {}; - -/* - * EMLINK is possible on filesystems that limit the number of hard links to a - * single file, like early versions of BtrFS - */ -TEST_F(Mkdir, emlink) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - mode_t mode = 0755; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_mkdir_in); - return (in->header.opcode == FUSE_MKDIR && - in->body.mkdir.mode == (S_IFDIR | mode) && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EMLINK))); - - ASSERT_NE(1, mkdir(FULLPATH, mode)); - ASSERT_EQ(EMLINK, errno); -} - -/* - * Creating a new directory after FUSE_LOOKUP returned a negative cache entry - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Mkdir, DISABLED_entry_cache_negative) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - /* - * Set entry_valid = 0 because this test isn't concerned with whether - * or not we actually cache negative entries, only with whether we - * interpret negative cache responses correctly. - */ - struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; - - /* mkdir will first do a LOOKUP, adding a negative cache entry */ - EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid)); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_MKDIR && - in->body.mkdir.mode == (S_IFDIR | mode) && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.create.entry.attr.mode = S_IFDIR | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); -} - -/* - * Creating a new directory should purge any negative namecache entries - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Mkdir, DISABLED_entry_cache_negative_purge) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; - - /* mkdir will first do a LOOKUP, adding a negative cache entry */ - EXPECT_LOOKUP(1, RELPATH).Times(1) - .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))) - .RetiresOnSaturation(); - - /* Then the MKDIR should purge the negative cache entry */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_MKDIR && - in->body.mkdir.mode == (S_IFDIR | mode) && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFDIR | mode; - out->body.entry.nodeid = ino; - out->body.entry.attr_valid = UINT64_MAX; - }))); - - ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); - - /* Finally, a subsequent lookup should query the daemon */ - expect_lookup(RELPATH, ino, S_IFDIR | mode, 0, 1); - - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); -} - -TEST_F(Mkdir, ok) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - mode_t mode = 0755; - uint64_t ino = 42; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_mkdir_in); - return (in->header.opcode == FUSE_MKDIR && - in->body.mkdir.mode == (S_IFDIR | mode) && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.create.entry.attr.mode = S_IFDIR | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/mkdir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/statfs.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/statfs.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/statfs.cc (nonexistent) @@ -1,118 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Statfs: public FuseTest {}; - -TEST_F(Statfs, eio) -{ - struct statfs statbuf; - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_STATFS); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EIO))); - - ASSERT_NE(0, statfs("mountpoint", &statbuf)); - ASSERT_EQ(EIO, errno); -} - -/* - * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes - * the statfs(2) response, which is necessary for unmounting. - */ -TEST_F(Statfs, enotconn) -{ - struct statfs statbuf; - char mp[PATH_MAX]; - - m_mock->kill_daemon(); - - ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno); - strlcat(mp, "/mountpoint", PATH_MAX); - ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); - - EXPECT_EQ(getuid(), statbuf.f_owner); - EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); - EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); - EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); -} - -TEST_F(Statfs, ok) -{ - struct statfs statbuf; - char mp[PATH_MAX]; - - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_STATFS); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, statfs); - out->body.statfs.st.blocks = 1000; - out->body.statfs.st.bfree = 100; - out->body.statfs.st.bavail = 200; - out->body.statfs.st.files = 5; - out->body.statfs.st.ffree = 6; - out->body.statfs.st.namelen = 128; - out->body.statfs.st.frsize = 1024; - }))); - - ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno); - strlcat(mp, "/mountpoint", PATH_MAX); - ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); - EXPECT_EQ(1024ul, statbuf.f_bsize); - /* - * fuse(4) ignores the filesystem's reported optimal transfer size, and - * chooses a size that works well with the rest of the system instead - */ - EXPECT_EQ(1000ul, statbuf.f_blocks); - EXPECT_EQ(100ul, statbuf.f_bfree); - EXPECT_EQ(200l, statbuf.f_bavail); - EXPECT_EQ(5ul, statbuf.f_files); - EXPECT_EQ(6l, statbuf.f_ffree); - EXPECT_EQ(128u, statbuf.f_namemax); - EXPECT_EQ(getuid(), statbuf.f_owner); - EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); - EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); - EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/statfs.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/create.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/create.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/create.cc (nonexistent) @@ -1,364 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Create: public FuseTest {}; - -/* - * If FUSE_CREATE sets the attr_valid, then subsequent GETATTRs should use the - * attribute cache - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ -TEST_F(Create, DISABLED_attr_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - int fd; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = S_IFREG | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).Times(0); - - fd = open(FULLPATH, O_CREAT | O_EXCL, mode); - EXPECT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * The fuse daemon fails the request with EEXIST. This usually indicates a - * race condition: some other FUSE client created the file in between when the - * kernel checked for it with lookup and tried to create it with create - */ -TEST_F(Create, eexist) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EEXIST))); - EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); - EXPECT_EQ(EEXIST, errno); -} - -/* - * If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback - * to FUSE_MKNOD/FUSE_OPEN - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Create, DISABLED_Enosys) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - int fd; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOSYS))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_mknod_in); - return (in->header.opcode == FUSE_MKNOD && - in->body.mknod.mode == (S_IFREG | mode) && - in->body.mknod.rdev == 0 && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = S_IFREG | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - }))); - - fd = open(FULLPATH, O_CREAT | O_EXCL, mode); - EXPECT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Creating a new file after FUSE_LOOKUP returned a negative cache entry - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Create, DISABLED_entry_cache_negative) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - int fd; - /* - * Set entry_valid = 0 because this test isn't concerned with whether - * or not we actually cache negative entries, only with whether we - * interpret negative cache responses correctly. - */ - struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; - - /* create will first do a LOOKUP, adding a negative cache entry */ - EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid)); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = S_IFREG | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - }))); - - fd = open(FULLPATH, O_CREAT | O_EXCL, mode); - ASSERT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Creating a new file should purge any negative namecache entries - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Create, DISABLED_entry_cache_negative_purge) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - int fd; - struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; - - /* create will first do a LOOKUP, adding a negative cache entry */ - EXPECT_LOOKUP(1, RELPATH).Times(1) - .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))) - .RetiresOnSaturation(); - - /* Then the CREATE should purge the negative cache entry */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = S_IFREG | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - }))); - - fd = open(FULLPATH, O_CREAT | O_EXCL, mode); - ASSERT_LE(0, fd) << strerror(errno); - - /* Finally, a subsequent lookup should query the daemon */ - expect_lookup(RELPATH, ino, S_IFREG | mode, 0, 1); - - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * The daemon is responsible for checking file permissions (unless the - * default_permissions mount option was used) - */ -TEST_F(Create, eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EPERM))); - EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); - EXPECT_EQ(EPERM, errno); -} - -TEST_F(Create, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = 0755; - uint64_t ino = 42; - int fd; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_open_in); - return (in->header.opcode == FUSE_CREATE && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = S_IFREG | mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - }))); - - fd = open(FULLPATH, O_CREAT | O_EXCL, mode); - EXPECT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/create.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/rmdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/rmdir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/rmdir.cc (nonexistent) @@ -1,91 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Rmdir: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - EXPECT_LOOKUP(1, relpath) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFDIR | 0755; - out->body.entry.nodeid = ino; - out->body.entry.attr.nlink = 2; - }))); -} -}; - -TEST_F(Rmdir, enotempty) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - 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 == 1); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOTEMPTY))); - - ASSERT_NE(0, rmdir(FULLPATH)); - ASSERT_EQ(ENOTEMPTY, errno); -} - -TEST_F(Rmdir, ok) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - 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 == 1); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(0))); - - ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/rmdir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/utils.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/utils.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/utils.cc (nonexistent) @@ -1,261 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include - -#include -#include - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -/* Check that fuse(4) is accessible and the current user can mount(2) */ -void check_environment() -{ - const char *mod_name = "fuse"; - const char *devnode = "/dev/fuse"; - const char *usermount_node = "vfs.usermount"; - int usermount_val = 0; - size_t usermount_size = sizeof(usermount_val); - if (modfind(mod_name) == -1) { - GTEST_SKIP() << "Module " << mod_name << - " could not be resolved"; - } - if (eaccess(devnode, R_OK | W_OK)) { - if (errno == ENOENT) { - GTEST_SKIP() << devnode << " does not exist"; - } else if (errno == EACCES) { - GTEST_SKIP() << devnode << - " is not accessible by the current user"; - } else { - GTEST_SKIP() << strerror(errno); - } - } - sysctlbyname(usermount_node, &usermount_val, &usermount_size, - NULL, 0); - if (geteuid() != 0 && !usermount_val) - GTEST_SKIP() << "current user is not allowed to mount"; -} - -class FuseEnv: public Environment { - virtual void SetUp() { - } -}; - -void FuseTest::SetUp() { - const char *node = "vfs.maxbcachebuf"; - int val = 0; - size_t size = sizeof(val); - - /* - * XXX check_environment should be called from FuseEnv::SetUp, but - * can't due to https://github.com/google/googletest/issues/2189 - */ - check_environment(); - if (IsSkipped()) - return; - - ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) - << strerror(errno); - m_maxbcachebuf = val; - - try { - m_mock = new MockFS(m_maxreadahead, m_push_symlinks_in, - m_default_permissions, m_init_flags); - } catch (std::system_error err) { - FAIL() << err.what(); - } -} - -void FuseTest::expect_getattr(uint64_t ino, uint64_t size) -{ - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - out->body.attr.attr.size = size; - out->body.attr.attr_valid = UINT64_MAX; - }))); -} - -void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode, - uint64_t size, int times) -{ - EXPECT_LOOKUP(1, relpath) - .Times(times) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = mode; - out->body.entry.nodeid = ino; - out->body.entry.attr.nlink = 1; - out->body.entry.attr_valid = UINT64_MAX; - out->body.entry.attr.size = size; - }))); -} - -void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).Times(times) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - out->body.open.fh = FH; - out->body.open.open_flags = flags; - }))); -} - -void FuseTest::expect_opendir(uint64_t ino) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_STATFS); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, statfs); - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPENDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - out->body.open.fh = FH; - }))); -} - -void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize, - uint64_t osize, const void *contents) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == FH && - in->body.read.offset == offset && - in->body.read.size == isize); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(struct fuse_out_header) + osize; - memmove(out->body.bytes, contents, osize); - }))).RetiresOnSaturation(); -} - -void FuseTest::expect_release(uint64_t ino, int times, uint64_t lock_owner, - int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_RELEASE && - in->header.nodeid == ino && - in->body.release.lock_owner == lock_owner && - in->body.release.fh == FH); - }, Eq(true)), - _) - ).Times(times) - .WillRepeatedly(Invoke(ReturnErrno(error))); -} -void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize, - uint64_t osize, uint32_t flags, const void *contents) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *buf = (const char*)in->body.bytes + - sizeof(struct fuse_write_in); - bool pid_ok; - - if (in->body.write.write_flags & FUSE_WRITE_CACHE) - pid_ok = true; - else - pid_ok = (pid_t)in->header.pid == getpid(); - - return (in->header.opcode == FUSE_WRITE && - in->header.nodeid == ino && - in->body.write.fh == FH && - in->body.write.offset == offset && - in->body.write.size == isize && - pid_ok && - in->body.write.write_flags == flags && - 0 == bcmp(buf, contents, isize)); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, write); - out->body.write.size = osize; - }))); -} - -static void usage(char* progname) { - fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname); - exit(2); -} - -int main(int argc, char **argv) { - int ch; - FuseEnv *fuse_env = new FuseEnv; - - InitGoogleTest(&argc, argv); - AddGlobalTestEnvironment(fuse_env); - - while ((ch = getopt(argc, argv, "v")) != -1) { - switch (ch) { - case 'v': - verbosity++; - break; - default: - usage(argv[0]); - break; - } - } - - return (RUN_ALL_TESTS()); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/utils.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/utils.hh =================================================================== --- projects/fuse2/tests/sys/fs/fuse/utils.hh (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/utils.hh (nonexistent) @@ -1,120 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * TODO: remove FUSE_WRITE_CACHE definition when upgrading to protocol 7.9. - * This bit was actually part of kernel protocol version 7.2, but never - * documented until 7.9 - */ -#ifndef FUSE_WRITE_CACHE -#define FUSE_WRITE_CACHE 1 -#endif - -class FuseTest : public ::testing::Test { - protected: - uint32_t m_maxreadahead; - uint32_t m_init_flags; - bool m_default_permissions; - bool m_push_symlinks_in; - MockFS *m_mock = NULL; - const static uint64_t FH = 0xdeadbeef1a7ebabe; - - public: - int m_maxbcachebuf; - - FuseTest(): - /* - * libfuse's default max_readahead is UINT_MAX, though it can - * be lowered - */ - m_maxreadahead(UINT_MAX), - m_init_flags(0), - m_default_permissions(false), - m_push_symlinks_in(false) - {} - - virtual void SetUp(); - - virtual void TearDown() { - if (m_mock) - delete m_mock; - } - - /* - * Create an expectation that FUSE_GETATTR will be called for the given - * inode any number of times. It will respond with a few basic - * attributes, like the given size and the mode S_IFREG | 0644 - */ - void expect_getattr(uint64_t ino, uint64_t size); - - /* - * Create an expectation that FUSE_LOOKUP will be called for the given - * path exactly times times. It will respond with inode ino, mode - * mode, filesize size, and cache validity forever. - */ - void expect_lookup(const char *relpath, uint64_t ino, mode_t mode, - uint64_t size, int times); - - /* - * Create an expectation that FUSE_GETATTR will be called for the given - * inode exactly times times. It will return with open_flags flags and - * file handle FH. - */ - void expect_open(uint64_t ino, uint32_t flags, int times); - - /* - * Create an expectation that FUSE_OPENDIR will be called exactly once - * for inode ino. - */ - void expect_opendir(uint64_t ino); - - /* - * Create an expectation that FUSE_READ will be called exactly once for - * the given inode, at offset offset and with size isize. It will - * return the first osize bytes from contents - */ - void expect_read(uint64_t ino, uint64_t offset, uint64_t isize, - uint64_t osize, const void *contents); - - /* - * Create an expectation that FUSE_RELEASE will be called times times - * for the given inode, returning error error - */ - void expect_release(uint64_t ino, int times, uint64_t lock_owner, - int error); - - /* - * Create an expectation that FUSE_WRITE will be called exactly once - * for the given inode, at offset offset, with write_flags flags, - * size isize and buffer contents. It will return osize - */ - void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, - uint64_t osize, uint32_t flags, const void *contents); -}; Index: projects/fuse2/tests/sys/fs/fuse/unlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/unlink.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/unlink.cc (nonexistent) @@ -1,102 +0,0 @@ -/*- - * Copyright (c) 2019 The FreeBSD Foundation - * All rights reserved. - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Unlink: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino, int times) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); -} - -void expect_unlink(uint64_t parent, const char *path, int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_UNLINK && - 0 == strcmp(path, in->body.unlink) && - in->header.nodeid == parent); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -}; - -TEST_F(Unlink, eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, 1); - expect_unlink(1, RELPATH, EPERM); - - ASSERT_NE(0, unlink(FULLPATH)); - ASSERT_EQ(EPERM, errno); -} - -TEST_F(Unlink, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, 1); - expect_unlink(1, RELPATH, 0); - - ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); -} - -/* Unlink an open file */ -TEST_F(Unlink, open_but_deleted) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino, 2); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_unlink(1, RELPATH, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/unlink.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/open.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/open.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/open.cc (nonexistent) @@ -1,185 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Open: public FuseTest { - -public: - -/* Test an OK open of a file with the given flags */ -void test_ok(int os_flags, int fuse_flags) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->body.open.flags == (uint32_t)fuse_flags && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { - out->header.len = sizeof(out->header); - SET_OUT_HEADER_LEN(out, open); - }))); - - /* Until the attr cache is working, we may send an additional GETATTR */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).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.mode = S_IFREG | 0644; - }))); - - fd = open(FULLPATH, os_flags); - EXPECT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} -}; - - -/* - * The fuse daemon fails the request with enoent. This usually indicates a - * race condition: some other FUSE client removed the file in between when the - * kernel checked for it with lookup and tried to open it - */ -TEST_F(Open, enoent) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOENT))); - EXPECT_NE(0, open(FULLPATH, O_RDONLY)); - EXPECT_EQ(ENOENT, errno); -} - -/* - * The daemon is responsible for checking file permissions (unless the - * default_permissions mount option was used) - */ -TEST_F(Open, eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPEN && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EPERM))); - EXPECT_NE(0, open(FULLPATH, O_RDONLY)); - EXPECT_EQ(EPERM, errno); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ -TEST_F(Open, DISABLED_o_append) -{ - test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND); -} - -/* The kernel is supposed to filter out this flag */ -TEST_F(Open, o_creat) -{ - test_ok(O_WRONLY | O_CREAT, O_WRONLY); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ -TEST_F(Open, DISABLED_o_direct) -{ - test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT); -} - -/* The kernel is supposed to filter out this flag */ -TEST_F(Open, o_excl) -{ - test_ok(O_WRONLY | O_EXCL, O_WRONLY); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236329 */ -TEST_F(Open, DISABLED_o_exec) -{ - test_ok(O_EXEC, O_EXEC); -} - -/* The kernel is supposed to filter out this flag */ -TEST_F(Open, o_noctty) -{ - test_ok(O_WRONLY | O_NOCTTY, O_WRONLY); -} - -TEST_F(Open, o_rdonly) -{ - test_ok(O_RDONLY, O_RDONLY); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ -TEST_F(Open, DISABLED_o_trunc) -{ - test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC); -} - -TEST_F(Open, o_wronly) -{ - test_ok(O_WRONLY, O_WRONLY); -} - -TEST_F(Open, o_rdwr) -{ - test_ok(O_RDWR, O_RDWR); -} - Property changes on: projects/fuse2/tests/sys/fs/fuse/open.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/xattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/xattr.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/xattr.cc (nonexistent) @@ -1,605 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -const char FULLPATH[] = "mountpoint/some_file.txt"; -const char RELPATH[] = "some_file.txt"; - -/* For testing filesystems without posix locking support */ -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) -{ - 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)), - _) - ).WillOnce(Invoke(r)) - .RetiresOnSaturation(); -} - -void expect_removexattr(uint64_t ino, const char *attr, int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *a = (const char*)in->body.bytes; - return (in->header.opcode == FUSE_REMOVEXATTR && - in->header.nodeid == ino && - 0 == strcmp(attr, a)); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -void expect_setxattr(uint64_t ino, const char *attr, const char *value, - ProcessMockerT r) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *a = (const char*)in->body.bytes + - sizeof(fuse_setxattr_in); - const char *v = a + strlen(a) + 1; - return (in->header.opcode == FUSE_SETXATTR && - in->header.nodeid == ino && - 0 == strcmp(attr, a) && - 0 == strcmp(value, v)); - }, Eq(true)), - _) - ).WillOnce(Invoke(r)); -} - -}; - -class Getxattr: public Xattr {}; -class Listxattr: public Xattr {}; -class Removexattr: public Xattr {}; -class Setxattr: public Xattr {}; - -/* - * If the extended attribute does not exist on this file, the daemon should - * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the - * correct errror code) - */ -TEST_F(Getxattr, enoattr) -{ - char data[80]; - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - 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); -} - -/* - * If the filesystem returns ENOSYS, then it will be treated as a permanent - * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP - * without querying the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Getxattr, DISABLED_enosys) -{ - char data[80]; - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS)); - - r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); - ASSERT_EQ(-1, r); - EXPECT_EQ(EOPNOTSUPP, errno); - - /* Subsequent attempts should not query the filesystem at all */ - r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); - ASSERT_EQ(-1, r); - EXPECT_EQ(EOPNOTSUPP, errno); -} - -/* - * On FreeBSD, if the user passes an insufficiently large buffer then the - * filesystem is supposed to copy as much of the attribute's value as will fit. - * - * On Linux, however, the filesystem is supposed to return ERANGE. - * - * libfuse specifies the Linux behavior. However, that's probably an error. - * 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(Getxattr, erange) -{ - char data[10]; - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE)); - - r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); - ASSERT_EQ(-1, r); - ASSERT_EQ(ERANGE, errno); -} - -/* - * If the user passes a 0-length buffer, then the daemon should just return the - * size of the attribute - */ -TEST_F(Getxattr, size_only) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_getxattr(ino, "user.foo", - ReturnImmediate([](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, getxattr); - out->body.getxattr.size = 99; - }) - ); - - ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0)) - << strerror(errno);; -} - -/* - * Successfully get an attribute from the system namespace - */ -TEST_F(Getxattr, system) -{ - uint64_t ino = 42; - char data[80]; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_SYSTEM; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_getxattr(ino, "system.foo", - ReturnImmediate([&](auto in __unused, auto out) { - memcpy((void*)out->body.bytes, value, value_len); - out->header.len = sizeof(out->header) + value_len; - }) - ); - - r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); - ASSERT_EQ(value_len, r) << strerror(errno); - EXPECT_STREQ(value, data); -} - -/* - * Successfully get an attribute from the user namespace - */ -TEST_F(Getxattr, user) -{ - uint64_t ino = 42; - char data[80]; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_getxattr(ino, "user.foo", - ReturnImmediate([&](auto in __unused, auto out) { - memcpy((void*)out->body.bytes, value, value_len); - out->header.len = sizeof(out->header) + value_len; - }) - ); - - r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); - ASSERT_EQ(value_len, r) << strerror(errno); - EXPECT_STREQ(value, data); -} - -/* - * If the filesystem returns ENOSYS, then it will be treated as a permanent - * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP - * without querying the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Listxattr, DISABLED_enosys) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnErrno(ENOSYS)); - - ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); - EXPECT_EQ(EOPNOTSUPP, errno); - - /* Subsequent attempts should not query the filesystem at all */ - ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); - EXPECT_EQ(EOPNOTSUPP, errno); -} - -/* - * Listing extended attributes failed because they aren't configured on this - * filesystem - */ -TEST_F(Listxattr, enotsup) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); - - ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); - ASSERT_EQ(ENOTSUP, errno); -} - -/* - * On FreeBSD, if the user passes an insufficiently large buffer then the - * filesystem is supposed to copy as much of the attribute's value as will fit. - * - * On Linux, however, the filesystem is supposed to return ERANGE. - * - * libfuse specifies the Linux behavior. However, that's probably an error. - * 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) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnErrno(ERANGE)); - - ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); - ASSERT_EQ(ERANGE, errno); -} - -/* - * Get the size of the list that it would take to list no extended attributes - */ -TEST_F(Listxattr, size_only_empty) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { - out->body.listxattr.size = 0; - SET_OUT_HEADER_LEN(out, listxattr); - })); - - ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) - << strerror(errno); -} - -/* - * Get the size of the list that it would take to list some extended - * attributes. Due to the format differences between a FreeBSD and a - * 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. - */ -TEST_F(Listxattr, size_only_nonempty) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { - out->body.listxattr.size = 45; - SET_OUT_HEADER_LEN(out, listxattr); - })); - - // TODO: fix the expected size after fixing the size calculation bug in - // fuse_vnop_listextattr. It should be exactly 45. - expect_listxattr(ino, 53, - ReturnImmediate([](auto in __unused, auto out) { - const char l[] = "user.foo"; - 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)) - << strerror(errno); -} - -TEST_F(Listxattr, size_only_really_big) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { - out->body.listxattr.size = 16000; - SET_OUT_HEADER_LEN(out, listxattr); - })); - - // TODO: fix the expected size after fixing the size calculation bug in - // fuse_vnop_listextattr. It should be exactly 16000. - expect_listxattr(ino, 16008, - ReturnImmediate([](auto in __unused, auto out) { - const char l[16] = "user.foobarbang"; - for (int i=0; i < 1000; i++) { - memcpy(&out->body.bytes[16 * i], l, 16); - } - out->header.len = sizeof(fuse_out_header) + 16000; - }) - ); - - ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0)) - << strerror(errno); -} - -/* - * List all of the user attributes of a file which has both user and system - * attributes - */ -TEST_F(Listxattr, user) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - char data[80]; - char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'}; - char attrs[28] = "user.foo\0system.x\0user.bang"; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, - ReturnImmediate([&](auto in __unused, auto out) { - out->body.listxattr.size = sizeof(attrs); - SET_OUT_HEADER_LEN(out, listxattr); - }) - ); - - // TODO: fix the expected size after fixing the size calculation bug in - // fuse_vnop_listextattr. - expect_listxattr(ino, sizeof(attrs) + 8, - 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((ssize_t)sizeof(expected), - extattr_list_file(FULLPATH, ns, data, sizeof(data))) - << strerror(errno); - ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); -} - -/* - * List all of the system attributes of a file which has both user and system - * attributes - */ -TEST_F(Listxattr, system) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_SYSTEM; - char data[80]; - char expected[2] = {1, 'x'}; - char attrs[28] = "user.foo\0system.x\0user.bang"; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_listxattr(ino, 0, - ReturnImmediate([&](auto in __unused, auto out) { - out->body.listxattr.size = sizeof(attrs); - SET_OUT_HEADER_LEN(out, listxattr); - }) - ); - - // TODO: fix the expected size after fixing the size calculation bug in - // fuse_vnop_listextattr. - expect_listxattr(ino, sizeof(attrs) + 8, - 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((ssize_t)sizeof(expected), - extattr_list_file(FULLPATH, ns, data, sizeof(data))) - << strerror(errno); - ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); -} - -/* Fail to remove a nonexistent attribute */ -TEST_F(Removexattr, enoattr) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_removexattr(ino, "user.foo", ENOATTR); - - ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); - ASSERT_EQ(ENOATTR, errno); -} - -/* - * If the filesystem returns ENOSYS, then it will be treated as a permanent - * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP - * without querying the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Removexattr, DISABLED_enosys) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_removexattr(ino, "user.foo", ENOSYS); - - ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); - EXPECT_EQ(EOPNOTSUPP, errno); - - /* Subsequent attempts should not query the filesystem at all */ - ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); - EXPECT_EQ(EOPNOTSUPP, errno); -} - -/* Successfully remove a user xattr */ -TEST_F(Removexattr, user) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_USER; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_removexattr(ino, "user.foo", 0); - - ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) - << strerror(errno); -} - -/* Successfully remove a system xattr */ -TEST_F(Removexattr, system) -{ - uint64_t ino = 42; - int ns = EXTATTR_NAMESPACE_SYSTEM; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_removexattr(ino, "system.foo", 0); - - ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) - << strerror(errno); -} - -/* - * If the filesystem returns ENOSYS, then it will be treated as a permanent - * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP - * without querying the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Setxattr, DISABLED_enosys) -{ - uint64_t ino = 42; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS)); - - r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); - ASSERT_EQ(-1, r); - EXPECT_EQ(EOPNOTSUPP, errno); - - /* Subsequent attempts should not query the filesystem at all */ - r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); - ASSERT_EQ(-1, r); - EXPECT_EQ(EOPNOTSUPP, errno); -} - -/* - * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem - * as currently configured doesn't support extended attributes. - */ -TEST_F(Setxattr, enotsup) -{ - uint64_t ino = 42; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP)); - - r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); - ASSERT_EQ(-1, r); - EXPECT_EQ(ENOTSUP, errno); -} - -/* - * Successfully set a user attribute. - */ -TEST_F(Setxattr, user) -{ - uint64_t ino = 42; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_USER; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_setxattr(ino, "user.foo", value, ReturnErrno(0)); - - r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); - ASSERT_EQ(value_len, r) << strerror(errno); -} - -/* - * Successfully set a system attribute. - */ -TEST_F(Setxattr, system) -{ - uint64_t ino = 42; - const char value[] = "whatever"; - ssize_t value_len = strlen(value) + 1; - int ns = EXTATTR_NAMESPACE_SYSTEM; - ssize_t r; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - expect_setxattr(ino, "system.foo", value, ReturnErrno(0)); - - r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); - ASSERT_EQ(value_len, r) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/xattr.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/link.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/link.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/link.cc (nonexistent) @@ -1,102 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Link: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} -}; - -TEST_F(Link, emlink) -{ - const char FULLPATH[] = "mountpoint/lnk"; - const char RELPATH[] = "lnk"; - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - uint64_t dst_ino = 42; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - expect_lookup(RELDST, dst_ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes - + sizeof(struct fuse_link_in); - return (in->header.opcode == FUSE_LINK && - in->body.link.oldnodeid == dst_ino && - (0 == strcmp(name, RELPATH))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EMLINK))); - - EXPECT_EQ(-1, link(FULLDST, FULLPATH)); - EXPECT_EQ(EMLINK, errno); -} - -TEST_F(Link, ok) -{ - const char FULLPATH[] = "mountpoint/src"; - const char RELPATH[] = "src"; - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - uint64_t dst_ino = 42; - const uint64_t ino = 42; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - expect_lookup(RELDST, dst_ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes - + sizeof(struct fuse_link_in); - return (in->header.opcode == FUSE_LINK && - in->body.link.oldnodeid == dst_ino && - (0 == strcmp(name, RELPATH))); - }, Eq(true)), - _) - ).WillOnce(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; - }))); - - EXPECT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/link.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/Makefile =================================================================== --- projects/fuse2/tests/sys/fs/fuse/Makefile (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/Makefile (nonexistent) @@ -1,208 +0,0 @@ -# $FreeBSD$ - -PACKAGE= tests - -TESTSDIR= ${TESTSBASE}/sys/fs/fuse - -# We could simply link all of these files into a single executable. But since -# Kyua treats googletest programs as plain tests, it's better to separate them -# out, so we get more granular reporting. -GTESTS+= access -GTESTS+= create -GTESTS+= default_permissions -GTESTS+= destroy -GTESTS+= flush -GTESTS+= fsync -GTESTS+= fsyncdir -GTESTS+= getattr -GTESTS+= interrupt -GTESTS+= link -GTESTS+= locks -GTESTS+= lookup -GTESTS+= mkdir -GTESTS+= mknod -GTESTS+= open -GTESTS+= opendir -GTESTS+= read -GTESTS+= readdir -GTESTS+= readlink -GTESTS+= release -GTESTS+= releasedir -GTESTS+= rename -GTESTS+= rmdir -GTESTS+= setattr -GTESTS+= statfs -GTESTS+= symlink -GTESTS+= unlink -GTESTS+= write -GTESTS+= xattr - -SRCS.access+= access.cc -SRCS.access+= getmntopts.c -SRCS.access+= mockfs.cc -SRCS.access+= utils.cc - -SRCS.create+= create.cc -SRCS.create+= getmntopts.c -SRCS.create+= mockfs.cc -SRCS.create+= utils.cc - -SRCS.default_permissions+= default_permissions.cc -SRCS.default_permissions+= getmntopts.c -SRCS.default_permissions+= mockfs.cc -SRCS.default_permissions+= utils.cc -TEST_METADATA.default_permissions+= required_user="unprivileged" - -SRCS.destroy+= destroy.cc -SRCS.destroy+= getmntopts.c -SRCS.destroy+= mockfs.cc -SRCS.destroy+= utils.cc - -SRCS.flush+= flush.cc -SRCS.flush+= getmntopts.c -SRCS.flush+= mockfs.cc -SRCS.flush+= utils.cc - -SRCS.fsync+= fsync.cc -SRCS.fsync+= getmntopts.c -SRCS.fsync+= mockfs.cc -SRCS.fsync+= utils.cc - -SRCS.fsyncdir+= fsyncdir.cc -SRCS.fsyncdir+= getmntopts.c -SRCS.fsyncdir+= mockfs.cc -SRCS.fsyncdir+= utils.cc - -SRCS.getattr+= getattr.cc -SRCS.getattr+= getmntopts.c -SRCS.getattr+= mockfs.cc -SRCS.getattr+= utils.cc - -SRCS.interrupt+= interrupt.cc -SRCS.interrupt+= getmntopts.c -SRCS.interrupt+= mockfs.cc -SRCS.interrupt+= utils.cc - -SRCS.link+= getmntopts.c -SRCS.link+= link.cc -SRCS.link+= mockfs.cc -SRCS.link+= utils.cc - -SRCS.locks+= locks.cc -SRCS.locks+= getmntopts.c -SRCS.locks+= mockfs.cc -SRCS.locks+= utils.cc - -SRCS.lookup+= getmntopts.c -SRCS.lookup+= lookup.cc -SRCS.lookup+= mockfs.cc -SRCS.lookup+= utils.cc - -SRCS.mkdir+= getmntopts.c -SRCS.mkdir+= mockfs.cc -SRCS.mkdir+= mkdir.cc -SRCS.mkdir+= utils.cc - -SRCS.mknod+= getmntopts.c -SRCS.mknod+= mockfs.cc -SRCS.mknod+= mknod.cc -SRCS.mknod+= utils.cc -TEST_METADATA.mknod+= required_user="root" - -SRCS.open+= getmntopts.c -SRCS.open+= mockfs.cc -SRCS.open+= open.cc -SRCS.open+= utils.cc - -SRCS.opendir+= getmntopts.c -SRCS.opendir+= mockfs.cc -SRCS.opendir+= opendir.cc -SRCS.opendir+= utils.cc - -SRCS.read+= getmntopts.c -SRCS.read+= mockfs.cc -SRCS.read+= read.cc -SRCS.read+= utils.cc - -SRCS.readdir+= getmntopts.c -SRCS.readdir+= mockfs.cc -SRCS.readdir+= readdir.cc -SRCS.readdir+= utils.cc - -SRCS.readlink+= getmntopts.c -SRCS.readlink+= mockfs.cc -SRCS.readlink+= readlink.cc -SRCS.readlink+= utils.cc - -SRCS.release+= getmntopts.c -SRCS.release+= mockfs.cc -SRCS.release+= release.cc -SRCS.release+= utils.cc - -SRCS.releasedir+= getmntopts.c -SRCS.releasedir+= mockfs.cc -SRCS.releasedir+= releasedir.cc -SRCS.releasedir+= utils.cc - -SRCS.rename+= getmntopts.c -SRCS.rename+= mockfs.cc -SRCS.rename+= rename.cc -SRCS.rename+= utils.cc - -SRCS.rmdir+= getmntopts.c -SRCS.rmdir+= mockfs.cc -SRCS.rmdir+= rmdir.cc -SRCS.rmdir+= utils.cc - -SRCS.setattr+= getmntopts.c -SRCS.setattr+= mockfs.cc -SRCS.setattr+= setattr.cc -SRCS.setattr+= utils.cc - -SRCS.statfs+= getmntopts.c -SRCS.statfs+= mockfs.cc -SRCS.statfs+= statfs.cc -SRCS.statfs+= utils.cc - -SRCS.symlink+= getmntopts.c -SRCS.symlink+= mockfs.cc -SRCS.symlink+= symlink.cc -SRCS.symlink+= utils.cc - -SRCS.unlink+= getmntopts.c -SRCS.unlink+= mockfs.cc -SRCS.unlink+= unlink.cc -SRCS.unlink+= utils.cc - -SRCS.write+= getmntopts.c -SRCS.write+= mockfs.cc -SRCS.write+= write.cc -SRCS.write+= utils.cc - -SRCS.xattr+= getmntopts.c -SRCS.xattr+= mockfs.cc -SRCS.xattr+= xattr.cc -SRCS.xattr+= utils.cc - -# TODO: drastically increase timeout after test development is mostly complete -TEST_METADATA+= timeout=10 - -FUSEFS= ${.CURDIR:H:H:H:H}/sys/fs/fuse -MOUNT= ${.CURDIR:H:H:H:H}/sbin/mount -CFLAGS+= -I${.CURDIR:H:H:H} -CFLAGS+= -I${FUSEFS} -CFLAGS+= -I${MOUNT} -.PATH: ${MOUNT} -CXXSTD= c++14 - -# XXX Setting CXXFLAGS globally seems to be necessary to get mockfs.cc and -# utils.cc to build correctly. -CXXFLAGS+= ${GTESTS_CXXFLAGS} - -LIBADD+= pthread -LIBADD+= gmock gtest -LIBADD+= util - -WARNS?= 6 - -.include Property changes on: projects/fuse2/tests/sys/fs/fuse/Makefile ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/mockfs.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/mockfs.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/mockfs.cc (nonexistent) @@ -1,447 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mntopts.h" // for build_iovec -} - -#include - -#include "mockfs.hh" - -using namespace testing; - -int verbosity = 0; -static sig_atomic_t quit = 0; - -const char* opcode2opname(uint32_t opcode) -{ - const int NUM_OPS = 39; - const char* table[NUM_OPS] = { - "Unknown (opcode 0)", - "LOOKUP", - "FORGET", - "GETATTR", - "SETATTR", - "READLINK", - "SYMLINK", - "Unknown (opcode 7)", - "MKNOD", - "MKDIR", - "UNLINK", - "RMDIR", - "RENAME", - "LINK", - "OPEN", - "READ", - "WRITE", - "STATFS", - "RELEASE", - "Unknown (opcode 19)", - "FSYNC", - "SETXATTR", - "GETXATTR", - "LISTXATTR", - "REMOVEXATTR", - "FLUSH", - "INIT", - "OPENDIR", - "READDIR", - "RELEASEDIR", - "FSYNCDIR", - "GETLK", - "SETLK", - "SETLKW", - "ACCESS", - "CREATE", - "INTERRUPT", - "BMAP", - "DESTROY" - }; - if (opcode >= NUM_OPS) - return ("Unknown (opcode > max)"); - else - return (table[opcode]); -} - -ProcessMockerT -ReturnErrno(int error) -{ - return([=](auto in, auto &out) { - auto out0 = new mockfs_buf_out; - out0->header.unique = in->header.unique; - out0->header.error = -error; - out0->header.len = sizeof(out0->header); - out.push_back(out0); - }); -} - -/* Helper function used for returning negative cache entries for LOOKUP */ -ProcessMockerT -ReturnNegativeCache(const struct timespec *entry_valid) -{ - return([=](auto in, auto &out) { - /* nodeid means ENOENT and cache it */ - auto out0 = new mockfs_buf_out; - out0->body.entry.nodeid = 0; - out0->header.unique = in->header.unique; - out0->header.error = 0; - out0->body.entry.entry_valid = entry_valid->tv_sec; - out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; - SET_OUT_HEADER_LEN(out0, entry); - out.push_back(out0); - }); -} - -ProcessMockerT -ReturnImmediate(std::function f) -{ - return([=](auto in, auto &out) { - auto out0 = new mockfs_buf_out; - out0->header.unique = in->header.unique; - f(in, out0); - out.push_back(out0); - }); -} - -void sigint_handler(int __unused sig) { - quit = 1; -} - -void debug_fuseop(const mockfs_buf_in *in) -{ - printf("%-11s ino=%2lu", opcode2opname(in->header.opcode), - in->header.nodeid); - if (verbosity > 1) { - printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u", - in->header.uid, in->header.gid, in->header.pid, - in->header.unique, in->header.len); - } - switch (in->header.opcode) { - case FUSE_FLUSH: - printf(" lock_owner=%lu", in->body.flush.lock_owner); - break; - case FUSE_FORGET: - printf(" nlookup=%lu", in->body.forget.nlookup); - break; - case FUSE_FSYNC: - printf(" flags=%#x", in->body.fsync.fsync_flags); - break; - case FUSE_FSYNCDIR: - printf(" flags=%#x", in->body.fsyncdir.fsync_flags); - break; - case FUSE_LOOKUP: - printf(" %s", in->body.lookup); - break; - case FUSE_OPEN: - printf(" flags=%#x mode=%#o", - in->body.open.flags, in->body.open.mode); - break; - case FUSE_OPENDIR: - printf(" flags=%#x mode=%#o", - in->body.opendir.flags, in->body.opendir.mode); - break; - case FUSE_READ: - printf(" offset=%lu size=%u", in->body.read.offset, - in->body.read.size); - break; - case FUSE_READDIR: - printf(" offset=%lu size=%u", in->body.readdir.offset, - in->body.readdir.size); - break; - case FUSE_RELEASE: - printf(" flags=%#x lock_owner=%lu", - in->body.release.flags, - in->body.release.lock_owner); - break; - case FUSE_SETATTR: - if (verbosity <= 1) { - printf(" valid=%#x", in->body.setattr.valid); - break; - } - if (in->body.setattr.valid & FATTR_MODE) - printf(" mode=%#o", in->body.setattr.mode); - if (in->body.setattr.valid & FATTR_UID) - printf(" uid=%u", in->body.setattr.uid); - if (in->body.setattr.valid & FATTR_GID) - printf(" gid=%u", in->body.setattr.gid); - if (in->body.setattr.valid & FATTR_SIZE) - printf(" size=%zu", in->body.setattr.size); - if (in->body.setattr.valid & FATTR_ATIME) - printf(" atime=%zu.%u", - in->body.setattr.atime, - in->body.setattr.atimensec); - if (in->body.setattr.valid & FATTR_MTIME) - printf(" mtime=%zu.%u", - in->body.setattr.mtime, - in->body.setattr.mtimensec); - if (in->body.setattr.valid & FATTR_FH) - printf(" fh=%zu", in->body.setattr.fh); - break; - case FUSE_SETXATTR: - /* - * In theory neither the xattr name and value need be - * ASCII, but in this test suite they always are. - */ - { - const char *attr = (const char*)in->body.bytes + - sizeof(fuse_setxattr_in); - const char *v = attr + strlen(attr) + 1; - printf(" %s=%s", attr, v); - } - break; - case FUSE_WRITE: - printf(" offset=%lu size=%u flags=%u", - in->body.write.offset, in->body.write.size, - in->body.write.write_flags); - break; - default: - break; - } - printf("\n"); -} - -MockFS::MockFS(int max_readahead, bool push_symlinks_in, - bool default_permissions, uint32_t flags) -{ - struct iovec *iov = NULL; - int iovlen = 0; - char fdstr[15]; - - m_daemon_id = NULL; - m_maxreadahead = max_readahead; - quit = 0; - - /* - * Kyua sets pwd to a testcase-unique tempdir; no need to use - * mkdtemp - */ - /* - * googletest doesn't allow ASSERT_ in constructors, so we must throw - * instead. - */ - if (mkdir("mountpoint" , 0644) && errno != EEXIST) - throw(std::system_error(errno, std::system_category(), - "Couldn't make mountpoint directory")); - - m_fuse_fd = open("/dev/fuse", O_RDWR); - if (m_fuse_fd < 0) - throw(std::system_error(errno, std::system_category(), - "Couldn't open /dev/fuse")); - sprintf(fdstr, "%d", m_fuse_fd); - - m_pid = getpid(); - - build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); - build_iovec(&iov, &iovlen, "fspath", - __DECONST(void *, "mountpoint"), -1); - build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); - build_iovec(&iov, &iovlen, "fd", fdstr, -1); - if (push_symlinks_in) { - const bool trueval = true; - build_iovec(&iov, &iovlen, "push_symlinks_in", - __DECONST(void*, &trueval), sizeof(bool)); - } - if (default_permissions) { - const bool trueval = true; - build_iovec(&iov, &iovlen, "default_permissions", - __DECONST(void*, &trueval), sizeof(bool)); - } - if (nmount(iov, iovlen, 0)) - throw(std::system_error(errno, std::system_category(), - "Couldn't mount filesystem")); - - // Setup default handler - ON_CALL(*this, process(_, _)) - .WillByDefault(Invoke(this, &MockFS::process_default)); - - init(flags); - signal(SIGUSR1, sigint_handler); - if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) - throw(std::system_error(errno, std::system_category(), - "Couldn't Couldn't start fuse thread")); -} - -MockFS::~MockFS() { - kill_daemon(); - ::unmount("mountpoint", MNT_FORCE); - if (m_daemon_id != NULL) { - pthread_join(m_daemon_id, NULL); - m_daemon_id = NULL; - } - rmdir("mountpoint"); -} - -void MockFS::init(uint32_t flags) { - mockfs_buf_in *in; - mockfs_buf_out *out; - - in = (mockfs_buf_in*) malloc(sizeof(*in)); - ASSERT_TRUE(in != NULL); - out = (mockfs_buf_out*) malloc(sizeof(*out)); - ASSERT_TRUE(out != NULL); - - read_request(in); - ASSERT_EQ(FUSE_INIT, in->header.opcode); - - memset(out, 0, sizeof(*out)); - out->header.unique = in->header.unique; - out->header.error = 0; - out->body.init.major = FUSE_KERNEL_VERSION; - out->body.init.minor = FUSE_KERNEL_MINOR_VERSION; - out->body.init.flags = in->body.init.flags & flags; - - /* - * The default max_write is set to this formula in libfuse, though - * individual filesystems can lower it. The "- 4096" was added in - * commit 154ffe2, with the commit message "fix". - */ - uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096; - /* For testing purposes, it should be distinct from MAXPHYS */ - m_max_write = MIN(default_max_write, MAXPHYS / 2); - out->body.init.max_write = m_max_write; - - out->body.init.max_readahead = m_maxreadahead; - SET_OUT_HEADER_LEN(out, init); - write(m_fuse_fd, out, out->header.len); - - free(in); -} - -void MockFS::kill_daemon() { - if (m_daemon_id != NULL) { - pthread_kill(m_daemon_id, SIGUSR1); - // Closing the /dev/fuse file descriptor first allows unmount - // to succeed even if the daemon doesn't correctly respond to - // commands during the unmount sequence. - close(m_fuse_fd); - } -} - -void MockFS::loop() { - mockfs_buf_in *in; - std::vector out; - - in = (mockfs_buf_in*) malloc(sizeof(*in)); - ASSERT_TRUE(in != NULL); - while (!quit) { - bzero(in, sizeof(*in)); - read_request(in); - if (quit) - break; - if (verbosity > 0) - debug_fuseop(in); - if (pid_ok((pid_t)in->header.pid)) { - process(in, out); - } else { - /* - * Reject any requests from unknown processes. Because - * we actually do mount a filesystem, plenty of - * unrelated system daemons may try to access it. - */ - process_default(in, out); - } - for (auto &it: out) { - ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 || - errno == EAGAIN) - << strerror(errno); - delete it; - } - out.clear(); - } - free(in); -} - -bool MockFS::pid_ok(pid_t pid) { - if (pid == m_pid) { - return (true); - } else { - struct kinfo_proc *ki; - bool ok = false; - - ki = kinfo_getproc(pid); - if (ki == NULL) - return (false); - /* - * Allow access by the aio daemon processes so that our tests - * can use aio functions - */ - if (0 == strncmp("aiod", ki->ki_comm, 4)) - ok = true; - free(ki); - return (ok); - } -} - -void MockFS::process_default(const mockfs_buf_in *in, - std::vector &out) -{ - auto out0 = new mockfs_buf_out; - out0->header.unique = in->header.unique; - out0->header.error = -EOPNOTSUPP; - out0->header.len = sizeof(out0->header); - out.push_back(out0); -} - -void MockFS::read_request(mockfs_buf_in *in) { - ssize_t res; - - res = read(m_fuse_fd, in, sizeof(*in)); - if (res < 0 && !quit) - perror("read"); - ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || quit); -} - -void* MockFS::service(void *pthr_data) { - MockFS *mock_fs = (MockFS*)pthr_data; - - mock_fs->loop(); - - return (NULL); -} - -void MockFS::unmount() { - ::unmount("mountpoint", 0); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/mockfs.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/default_permissions.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/default_permissions.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/default_permissions.cc (nonexistent) @@ -1,137 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * Tests for the "default_permissions" mount option. They must be in their own - * file so they can be run as an unprivileged user - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class DefaultPermissions: public FuseTest { - -virtual void SetUp() { - FuseTest::SetUp(); - - if (geteuid() == 0) { - GTEST_SKIP() << "This test requires an unprivileged user"; - } - m_default_permissions = true; -} - -public: -void expect_lookup(const char *relpath, uint64_t ino, mode_t mode) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | mode, 0, 1); -} - -}; - -class Access: public DefaultPermissions {}; -class Open: public DefaultPermissions {}; - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ -TEST_F(Access, DISABLED_eaccess) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - mode_t access_mode = X_OK; - - expect_lookup(RELPATH, ino, S_IFREG | 0644); - /* - * Once default_permissions is properly implemented, there might be - * another FUSE_GETATTR or something in here. But there should not be - * a FUSE_ACCESS - */ - - ASSERT_NE(0, access(FULLPATH, access_mode)); - ASSERT_EQ(EACCES, errno); -} - -TEST_F(Access, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - mode_t access_mode = R_OK; - - expect_lookup(RELPATH, ino, S_IFREG | 0644); - /* - * Once default_permissions is properly implemented, there might be - * another FUSE_GETATTR or something in here. But there should not be - * a FUSE_ACCESS - */ - - ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); -} - -TEST_F(Open, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino, S_IFREG | 0644); - expect_open(ino, 0, 1); - /* Until the attr cache is working, we may send an additional GETATTR */ - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_RDONLY); - EXPECT_LE(0, fd) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ -TEST_F(Open, DISABLED_eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, S_IFREG | 0644); - /* - * Once default_permissions is properly implemented, there might be - * another FUSE_GETATTR or something in here. But there should not be - * a FUSE_ACCESS - */ - - EXPECT_NE(0, open(FULLPATH, O_RDWR)); - EXPECT_EQ(EPERM, errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/default_permissions.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/release.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/release.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/release.cc (nonexistent) @@ -1,189 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Release: public FuseTest { - -public: -void expect_lookup(const char *relpath, uint64_t ino, int times) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); -} -}; - -class ReleaseWithLocks: public Release { - virtual void SetUp() { - m_init_flags = FUSE_POSIX_LOCKS; - Release::SetUp(); - } -}; - - -/* If a file descriptor is duplicated, only the last close causes RELEASE */ -TEST_F(Release, dup) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd, fd2; - - expect_lookup(RELPATH, ino, 1); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_release(ino, 1, 0, 0); - - fd = open(FULLPATH, O_RDONLY); - EXPECT_LE(0, fd) << strerror(errno); - - fd2 = dup(fd); - - ASSERT_EQ(0, close(fd2)) << strerror(errno); - ASSERT_EQ(0, close(fd)) << strerror(errno); -} - -/* - * Some FUSE filesystem cache data internally and flush it on release. Such - * filesystems may generate errors during release. On Linux, these get - * returned by close(2). However, POSIX does not require close(2) to return - * this error. FreeBSD's fuse(4) should return EIO if it returns an error at - * all. - */ -/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ -TEST_F(Release, eio) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino, 1); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_release(ino, 1, 0, EIO); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); -} - -/* - * fuse(4) will issue multiple FUSE_OPEN operations for the same file if it's - * opened with different modes. Each FUSE_OPEN should get its own - * FUSE_RELEASE. - */ -TEST_F(Release, multiple_opens) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd, fd2; - - expect_lookup(RELPATH, ino, 2); - expect_open(ino, 0, 2); - expect_getattr(ino, 0); - expect_release(ino, 2, 0, 0); - - fd = open(FULLPATH, O_RDONLY); - EXPECT_LE(0, fd) << strerror(errno); - - fd2 = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd2) << strerror(errno); - - ASSERT_EQ(0, close(fd2)) << strerror(errno); - ASSERT_EQ(0, close(fd)) << strerror(errno); -} - -TEST_F(Release, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino, 1); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_release(ino, 1, 0, 0); - - fd = open(FULLPATH, O_RDONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(0, close(fd)) << strerror(errno); -} - -/* When closing a file with a POSIX file lock, release should release the lock*/ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(ReleaseWithLocks, DISABLED_unlock_on_close) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - struct flock fl; - pid_t pid = getpid(); - - expect_lookup(RELPATH, ino, 1); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlk.fh == FH); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, setlk); - out->body.setlk.lk = in->body.setlk.lk; - }))); - expect_release(ino, 1, (uint64_t)pid, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 0; - fl.l_len = 0; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); - - ASSERT_EQ(0, close(fd)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/release.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/mockfs.hh =================================================================== --- projects/fuse2/tests/sys/fs/fuse/mockfs.hh (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/mockfs.hh (nonexistent) @@ -1,239 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include - -#include - -#include "fuse_kernel.h" -} - -#include - -#define TIME_T_MAX (std::numeric_limits::max()) - -/* - * A pseudo-fuse errno used indicate that a fuse operation should have no - * response, at least not immediately - */ -#define FUSE_NORESPONSE 9999 - -#define SET_OUT_HEADER_LEN(out, variant) { \ - (out)->header.len = (sizeof((out)->header) + \ - sizeof((out)->body.variant)); \ -} - -/* - * Create an expectation on FUSE_LOOKUP and return it so the caller can set - * actions. - * - * This must be a macro instead of a method because EXPECT_CALL returns a type - * with a deleted constructor. - */ -#define EXPECT_LOOKUP(parent, path) \ - EXPECT_CALL(*m_mock, process( \ - ResultOf([=](auto in) { \ - return (in->header.opcode == FUSE_LOOKUP && \ - in->header.nodeid == (parent) && \ - strcmp(in->body.lookup, (path)) == 0); \ - }, Eq(true)), \ - _) \ - ) - -extern int verbosity; - -/* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */ -struct fuse_create_out { - struct fuse_entry_out entry; - struct fuse_open_out open; -}; - -union fuse_payloads_in { - fuse_access_in access; - /* value is from fuse_kern_chan.c in fusefs-libs */ - uint8_t bytes[0x21000 - sizeof(struct fuse_in_header)]; - fuse_flush_in flush; - fuse_fsync_in fsync; - fuse_fsync_in fsyncdir; - fuse_forget_in forget; - fuse_interrupt_in interrupt; - fuse_lk_in getlk; - fuse_getxattr_in getxattr; - fuse_init_in init; - fuse_link_in link; - fuse_listxattr_in listxattr; - char lookup[0]; - fuse_mkdir_in mkdir; - fuse_mknod_in mknod; - fuse_open_in open; - fuse_open_in opendir; - fuse_read_in read; - fuse_read_in readdir; - fuse_release_in release; - fuse_release_in releasedir; - fuse_rename_in rename; - char rmdir[0]; - fuse_setattr_in setattr; - fuse_setxattr_in setxattr; - fuse_lk_in setlk; - fuse_lk_in setlkw; - char unlink[0]; - fuse_write_in write; -}; - -struct mockfs_buf_in { - fuse_in_header header; - union fuse_payloads_in body; -}; - -union fuse_payloads_out { - fuse_attr_out attr; - fuse_create_out create; - /* The protocol places no limits on the size of bytes */ - uint8_t bytes[0x20000]; - fuse_entry_out entry; - fuse_lk_out getlk; - fuse_getxattr_out getxattr; - fuse_init_out init; - fuse_listxattr_out listxattr; - fuse_open_out open; - fuse_lk_out setlk; - fuse_lk_out setlkw; - fuse_statfs_out statfs; - /* - * The protocol places no limits on the length of the string. This is - * merely convenient for testing. - */ - char str[80]; - fuse_write_out write; -}; - -struct mockfs_buf_out { - fuse_out_header header; - union fuse_payloads_out body; - - /* Default constructor: zero everything */ - mockfs_buf_out() { - memset(this, 0, sizeof(*this)); - } -}; - -/* A function that can be invoked in place of MockFS::process */ -typedef std::function &out)> -ProcessMockerT; - -/* - * Helper function used for setting an error expectation for any fuse operation. - * The operation will return the supplied error - */ -ProcessMockerT ReturnErrno(int error); - -/* Helper function used for returning negative cache entries for LOOKUP */ -ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid); - -/* Helper function used for returning a single immediate response */ -ProcessMockerT ReturnImmediate( - std::function f); - -/* - * Fake FUSE filesystem - * - * "Mounts" a filesystem to a temporary directory and services requests - * according to the programmed expectations. - * - * Operates directly on the fuse(4) kernel API, not the libfuse(3) user api. - */ -class MockFS { - /* - * thread id of the fuse daemon thread - * - * It must run in a separate thread so it doesn't deadlock with the - * client test code. - */ - pthread_t m_daemon_id; - - /* file descriptor of /dev/fuse control device */ - int m_fuse_fd; - - /* The max_readahead filesystem option */ - uint32_t m_maxreadahead; - - /* pid of the test process */ - pid_t m_pid; - - /* Initialize a session after mounting */ - void init(uint32_t flags); - - /* Is pid from a process that might be involved in the test? */ - bool pid_ok(pid_t pid); - - /* Default request handler */ - void process_default(const mockfs_buf_in*, - std::vector&); - - /* Entry point for the daemon thread */ - static void* service(void*); - - /* Read, but do not process, a single request from the kernel */ - void read_request(mockfs_buf_in*); - - public: - /* Maximum size of a FUSE_WRITE write */ - uint32_t m_max_write; - - /* Create a new mockfs and mount it to a tempdir */ - MockFS(int max_readahead, bool push_symlinks_in, - bool default_permissions, uint32_t flags); - virtual ~MockFS(); - - /* Kill the filesystem daemon without unmounting the filesystem */ - void kill_daemon(); - - /* Process FUSE requests endlessly */ - void loop(); - - /* - * Request handler - * - * This method is expected to provide the responses to each FUSE - * operation. For an immediate response, push one buffer into out. - * For a delayed response, push nothing. For an immediate response - * plus a delayed response to an earlier operation, push two bufs. - * Test cases must define each response using Googlemock expectations - */ - MOCK_METHOD2(process, void(const mockfs_buf_in*, - std::vector&)); - - /* Gracefully unmount */ - void unmount(); -}; Index: projects/fuse2/tests/sys/fs/fuse/opendir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/opendir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/opendir.cc (nonexistent) @@ -1,147 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Opendir: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); -} -}; - - -/* - * The fuse daemon fails the request with enoent. This usually indicates a - * race condition: some other FUSE client removed the file in between when the - * kernel checked for it with lookup and tried to open it - */ -TEST_F(Opendir, enoent) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPENDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOENT))); - EXPECT_NE(0, open(FULLPATH, O_DIRECTORY)); - EXPECT_EQ(ENOENT, errno); -} - -/* - * The daemon is responsible for checking file permissions (unless the - * default_permissions mount option was used) - */ -TEST_F(Opendir, eperm) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPENDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EPERM))); - - EXPECT_NE(0, open(FULLPATH, O_DIRECTORY)); - EXPECT_EQ(EPERM, errno); -} - -TEST_F(Opendir, open) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPENDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, open); - }))); - - EXPECT_LE(0, open(FULLPATH, O_DIRECTORY)) << strerror(errno); -} - -TEST_F(Opendir, opendir) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino); - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_STATFS); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, statfs); - }))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_OPENDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, open); - }))); - - errno = 0; - EXPECT_NE(NULL, opendir(FULLPATH)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/opendir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/readdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/readdir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/readdir.cc (nonexistent) @@ -1,299 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; -using namespace std; - -class Readdir: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); -} - -void expect_readdir(uint64_t ino, uint64_t off, vector &ents) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READDIR && - in->header.nodeid == ino && - in->body.readdir.offset == off); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) { - struct fuse_dirent *fde = (struct fuse_dirent*)out->body.bytes; - int i = 0; - - out->header.error = 0; - out->header.len = 0; - - for (const auto& it: ents) { - size_t entlen, entsize; - - fde->ino = it.d_fileno; - fde->off = it.d_off; - fde->type = it.d_type; - fde->namelen = it.d_namlen; - strncpy(fde->name, it.d_name, it.d_namlen); - entlen = FUSE_NAME_OFFSET + fde->namelen; - entsize = FUSE_DIRENT_SIZE(fde); - /* - * The FUSE protocol does not require zeroing out the - * unused portion of the name. But it's a good - * practice to prevent information disclosure to the - * FUSE client, even though the client is usually the - * kernel - */ - memset(fde->name + fde->namelen, 0, entsize - entlen); - if (out->header.len + entsize > in->body.read.size) { - printf("Overflow in readdir expectation: i=%d\n" - , i); - break; - } - out->header.len += entsize; - fde = (struct fuse_dirent*) - ((long*)fde + entsize / sizeof(long)); - i++; - } - out->header.len += sizeof(out->header); - }))); - -} -}; - -/* FUSE_READDIR returns nothing but "." and ".." */ -TEST_F(Readdir, dots) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - DIR *dir; - struct dirent *de; - vector ents(2); - vector empty_ents(0); - const char *dot = "."; - const char *dotdot = ".."; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - ents[0].d_fileno = 2; - ents[0].d_off = 2000; - ents[0].d_namlen = strlen(dotdot); - ents[0].d_type = DT_DIR; - strncpy(ents[0].d_name, dotdot, ents[0].d_namlen); - ents[1].d_fileno = 3; - ents[1].d_off = 3000; - ents[1].d_namlen = strlen(dot); - ents[1].d_type = DT_DIR; - strncpy(ents[1].d_name, dot, ents[1].d_namlen); - expect_readdir(ino, 0, ents); - expect_readdir(ino, 3000, empty_ents); - - errno = 0; - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - - errno = 0; - de = readdir(dir); - ASSERT_NE(NULL, de) << strerror(errno); - EXPECT_EQ(2ul, de->d_fileno); - /* - * fuse(4) doesn't actually set d_off, which is ok for now because - * nothing uses it. - */ - //EXPECT_EQ(2000, de->d_off); - EXPECT_EQ(DT_DIR, de->d_type); - EXPECT_EQ(2, de->d_namlen); - EXPECT_EQ(0, strcmp("..", de->d_name)); - - errno = 0; - de = readdir(dir); - ASSERT_NE(NULL, de) << strerror(errno); - EXPECT_EQ(3ul, de->d_fileno); - //EXPECT_EQ(3000, de->d_off); - EXPECT_EQ(DT_DIR, de->d_type); - EXPECT_EQ(1, de->d_namlen); - EXPECT_EQ(0, strcmp(".", de->d_name)); - - ASSERT_EQ(NULL, readdir(dir)); - ASSERT_EQ(0, errno); - - /* Deliberately leak dir. RELEASEDIR will be tested separately */ -} - -TEST_F(Readdir, eio) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - DIR *dir; - struct dirent *de; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READDIR && - in->header.nodeid == ino && - in->body.readdir.offset == 0); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EIO))); - - errno = 0; - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - - errno = 0; - de = readdir(dir); - ASSERT_EQ(NULL, de); - ASSERT_EQ(EIO, errno); - - /* Deliberately leak dir. RELEASEDIR will be tested separately */ -} - -/* - * FUSE_READDIR returns nothing, not even "." and "..". This is legal, though - * the filesystem obviously won't be fully functional. - */ -TEST_F(Readdir, nodots) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - DIR *dir; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READDIR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.error = 0; - out->header.len = sizeof(out->header); - }))); - - errno = 0; - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - errno = 0; - ASSERT_EQ(NULL, readdir(dir)); - ASSERT_EQ(0, errno); - - /* Deliberately leak dir. RELEASEDIR will be tested separately */ -} - -/* telldir(3) and seekdir(3) should work with fuse */ -TEST_F(Readdir, seekdir) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - DIR *dir; - struct dirent *de; - /* - * use enough entries to be > 4096 bytes, so getdirentries must be - * called - * multiple times. - */ - vector ents0(122), ents1(102), ents2(30); - long bookmark; - int i = 0; - - for (auto& it: ents0) { - snprintf(it.d_name, MAXNAMLEN, "file.%d", i); - it.d_fileno = 2 + i; - it.d_off = (2 + i) * 1000; - it.d_namlen = strlen(it.d_name); - it.d_type = DT_REG; - i++; - } - for (auto& it: ents1) { - snprintf(it.d_name, MAXNAMLEN, "file.%d", i); - it.d_fileno = 2 + i; - it.d_off = (2 + i) * 1000; - it.d_namlen = strlen(it.d_name); - it.d_type = DT_REG; - i++; - } - for (auto& it: ents2) { - snprintf(it.d_name, MAXNAMLEN, "file.%d", i); - it.d_fileno = 2 + i; - it.d_off = (2 + i) * 1000; - it.d_namlen = strlen(it.d_name); - it.d_type = DT_REG; - i++; - } - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - - expect_readdir(ino, 0, ents0); - expect_readdir(ino, 123000, ents1); - expect_readdir(ino, 225000, ents2); - - errno = 0; - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - - for (i=0; i < 128; i++) { - errno = 0; - de = readdir(dir); - ASSERT_NE(NULL, de) << strerror(errno); - EXPECT_EQ(2 + (ino_t)i, de->d_fileno); - } - bookmark = telldir(dir); - - for (; i < 232; i++) { - errno = 0; - de = readdir(dir); - ASSERT_NE(NULL, de) << strerror(errno); - EXPECT_EQ(2 + (ino_t)i, de->d_fileno); - } - - seekdir(dir, bookmark); - de = readdir(dir); - ASSERT_NE(NULL, de) << strerror(errno); - EXPECT_EQ(130ul, de->d_fileno); - - /* Deliberately leak dir. RELEASEDIR will be tested separately */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/readdir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/fsyncdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/fsyncdir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/fsyncdir.cc (nonexistent) @@ -1,191 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -/* - * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28. - * This bit was actually part of kernel protocol version 5.2, but never - * documented until after 7.28 - */ -#ifndef FUSE_FSYNC_FDATASYNC -#define FUSE_FSYNC_FDATASYNC 1 -#endif - -class FsyncDir: public FuseTest { -public: -void expect_fsyncdir(uint64_t ino, uint32_t flags, int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_FSYNCDIR && - in->header.nodeid == ino && - //(pid_t)in->header.pid == getpid() && - in->body.fsyncdir.fh == FH && - in->body.fsyncdir.fsync_flags == flags); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); -} - -}; - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(FsyncDir, DISABLED_aio_fsync) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - struct aiocb iocb, *piocb; - int fd; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_fsyncdir(ino, 0, 0); - - fd = open(FULLPATH, O_DIRECTORY); - ASSERT_LE(0, fd) << strerror(errno); - - bzero(&iocb, sizeof(iocb)); - iocb.aio_fildes = fd; - - ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno); - ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(FsyncDir, DISABLED_eio) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_fsyncdir(ino, 0, EIO); - - fd = open(FULLPATH, O_DIRECTORY); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_NE(0, fsync(fd)); - ASSERT_EQ(EIO, errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the filesystem returns ENOSYS, it will be treated as success and - * subsequent calls to VOP_FSYNC will succeed automatically without being sent - * to the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(FsyncDir, DISABLED_enosys) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_fsyncdir(ino, FUSE_FSYNC_FDATASYNC, ENOSYS); - - fd = open(FULLPATH, O_DIRECTORY); - ASSERT_LE(0, fd) << strerror(errno); - EXPECT_EQ(0, fsync(fd)) << strerror(errno); - - /* Subsequent calls shouldn't query the daemon*/ - EXPECT_EQ(0, fsync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(FsyncDir, DISABLED_fsyncdata) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_fsyncdir(ino, FUSE_FSYNC_FDATASYNC, 0); - - fd = open(FULLPATH, O_DIRECTORY); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, fdatasync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Unlike regular files, the kernel doesn't know whether a directory is or - * isn't dirty, so fuse(4) should always send FUSE_FSYNCDIR on fsync(2) - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(FsyncDir, DISABLED_fsync) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_fsyncdir(ino, 0, 0); - - fd = open(FULLPATH, O_DIRECTORY); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, fsync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/fsyncdir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/symlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/symlink.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/symlink.cc (nonexistent) @@ -1,90 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Symlink: public FuseTest {}; - -TEST_F(Symlink, enospc) -{ - const char FULLPATH[] = "mountpoint/lnk"; - const char RELPATH[] = "lnk"; - const char dst[] = "dst"; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes; - const char *linkname = name + strlen(name) + 1; - return (in->header.opcode == FUSE_SYMLINK && - (0 == strcmp(linkname, dst)) && - (0 == strcmp(name, RELPATH))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOSPC))); - - EXPECT_EQ(-1, symlink(dst, FULLPATH)); - EXPECT_EQ(ENOSPC, errno); -} - -TEST_F(Symlink, ok) -{ - const char FULLPATH[] = "mountpoint/src"; - const char RELPATH[] = "src"; - const char dst[] = "dst"; - const uint64_t ino = 42; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes; - const char *linkname = name + strlen(name) + 1; - return (in->header.opcode == FUSE_SYMLINK && - (0 == strcmp(linkname, dst)) && - (0 == strcmp(name, RELPATH))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFLNK | 0777; - out->body.entry.nodeid = ino; - }))); - - EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/symlink.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/rename.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/rename.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/rename.cc (nonexistent) @@ -1,249 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Rename: public FuseTest { - public: - int tmpfd = -1; - char tmpfile[80] = "/tmp/fuse.rename.XXXXXX"; - - virtual void TearDown() { - if (tmpfd >= 0) { - close(tmpfd); - unlink(tmpfile); - } - - FuseTest::TearDown(); - } -}; - -// EINVAL, dst is subdir of src -TEST_F(Rename, einval) -{ - const char FULLDST[] = "mountpoint/src/dst"; - const char RELDST[] = "dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - uint64_t src_ino = 42; - - expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2); - EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); - - ASSERT_NE(0, rename(FULLSRC, FULLDST)); - ASSERT_EQ(EINVAL, errno); -} - -// source does not exist -TEST_F(Rename, enoent) -{ - const char FULLDST[] = "mountpoint/dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - // FUSE hardcodes the mountpoint to inode 1 - - EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke(ReturnErrno(ENOENT))); - - ASSERT_NE(0, rename(FULLSRC, FULLDST)); - ASSERT_EQ(ENOENT, errno); -} - -/* - * Renaming a file after FUSE_LOOKUP returned a negative cache entry for dst - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Rename, DISABLED_entry_cache_negative) -{ - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - // FUSE hardcodes the mountpoint to inode 1 - uint64_t dst_dir_ino = 1; - uint64_t ino = 42; - /* - * Set entry_valid = 0 because this test isn't concerned with whether - * or not we actually cache negative entries, only with whether we - * interpret negative cache responses correctly. - */ - struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; - - expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); - /* LOOKUP returns a negative cache entry for dst */ - EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid)); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *src = (const char*)in->body.bytes + - sizeof(fuse_rename_in); - const char *dst = src + strlen(src) + 1; - return (in->header.opcode == FUSE_RENAME && - in->body.rename.newdir == dst_dir_ino && - (0 == strcmp(RELDST, dst)) && - (0 == strcmp(RELSRC, src))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(0))); - - ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); -} - -/* - * Renaming a file should purge any negative namecache entries for the dst - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ -TEST_F(Rename, DISABLED_entry_cache_negative_purge) -{ - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - // FUSE hardcodes the mountpoint to inode 1 - uint64_t dst_dir_ino = 1; - uint64_t ino = 42; - /* - * Set entry_valid = 0 because this test isn't concerned with whether - * or not we actually cache negative entries, only with whether we - * interpret negative cache responses correctly. - */ - struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; - - expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); - /* LOOKUP returns a negative cache entry for dst */ - EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid)) - .RetiresOnSaturation(); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *src = (const char*)in->body.bytes + - sizeof(fuse_rename_in); - const char *dst = src + strlen(src) + 1; - return (in->header.opcode == FUSE_RENAME && - in->body.rename.newdir == dst_dir_ino && - (0 == strcmp(RELDST, dst)) && - (0 == strcmp(RELSRC, src))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(0))); - - ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); - - /* Finally, a subsequent lookup should query the daemon */ - expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); - - ASSERT_EQ(0, access(FULLDST, F_OK)) << strerror(errno); -} - -TEST_F(Rename, exdev) -{ - const char FULLB[] = "mountpoint/src"; - const char RELB[] = "src"; - // FUSE hardcodes the mountpoint to inode 1 - uint64_t b_ino = 42; - - tmpfd = mkstemp(tmpfile); - ASSERT_LE(0, tmpfd) << strerror(errno); - - expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2); - - ASSERT_NE(0, rename(tmpfile, FULLB)); - ASSERT_EQ(EXDEV, errno); - - ASSERT_NE(0, rename(FULLB, tmpfile)); - ASSERT_EQ(EXDEV, errno); -} - -TEST_F(Rename, ok) -{ - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - // FUSE hardcodes the mountpoint to inode 1 - uint64_t dst_dir_ino = 1; - uint64_t ino = 42; - - expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); - EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *src = (const char*)in->body.bytes + - sizeof(fuse_rename_in); - const char *dst = src + strlen(src) + 1; - return (in->header.opcode == FUSE_RENAME && - in->body.rename.newdir == dst_dir_ino && - (0 == strcmp(RELDST, dst)) && - (0 == strcmp(RELSRC, src))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(0))); - - ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); -} - -// Rename overwrites an existing destination file -TEST_F(Rename, overwrite) -{ - const char FULLDST[] = "mountpoint/dst"; - const char RELDST[] = "dst"; - const char FULLSRC[] = "mountpoint/src"; - const char RELSRC[] = "src"; - // The inode of the already-existing destination file - uint64_t dst_ino = 2; - // FUSE hardcodes the mountpoint to inode 1 - uint64_t dst_dir_ino = 1; - uint64_t ino = 42; - - expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); - expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *src = (const char*)in->body.bytes + - sizeof(fuse_rename_in); - const char *dst = src + strlen(src) + 1; - return (in->header.opcode == FUSE_RENAME && - in->body.rename.newdir == dst_dir_ino && - (0 == strcmp(RELDST, dst)) && - (0 == strcmp(RELSRC, src))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(0))); - - ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/rename.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/destroy.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/destroy.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/destroy.cc (nonexistent) @@ -1,92 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Destroy: public FuseTest { -public: -void expect_destroy(int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_DESTROY); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -void expect_forget(uint64_t ino, uint64_t nlookup) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_FORGET && - in->header.nodeid == ino && - in->body.forget.nlookup == nlookup); - }, Eq(true)), - _) - ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { - /* FUSE_FORGET has no response! */ - })); -} -}; - -/* - * On unmount the kernel should send a FUSE_DESTROY operation. It should also - * send FUSE_FORGET operations for all inodes with lookup_count > 0. It's hard - * to trigger FUSE_FORGET in way except by unmounting, so this is the only - * testing that FUSE_FORGET gets. - */ -TEST_F(Destroy, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); - expect_forget(1, 1); - expect_forget(ino, 2); - expect_destroy(0); - - /* - * access(2) the file to force a lookup. Access it twice to double its - * lookup count. - */ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - - /* - * Unmount, triggering a FUSE_DESTROY and also causing a VOP_RECLAIM - * for every vnode on this mp, triggering FUSE_FORGET for each of them. - */ - m_mock->unmount(); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/destroy.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/lookup.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/lookup.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/lookup.cc (nonexistent) @@ -1,291 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Lookup: public FuseTest {}; - -/* - * If lookup returns a non-zero cache timeout, then subsequent VOP_GETATTRs - * should use the cached attributes, rather than query the daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ -TEST_F(Lookup, DISABLED_attr_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - const uint64_t generation = 13; - struct stat sb; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.nodeid = ino; - out->body.entry.attr_valid = UINT64_MAX; - out->body.entry.attr.ino = ino; // Must match nodeid - out->body.entry.attr.mode = S_IFREG | 0644; - out->body.entry.attr.size = 1; - out->body.entry.attr.blocks = 2; - out->body.entry.attr.atime = 3; - out->body.entry.attr.mtime = 4; - out->body.entry.attr.ctime = 5; - out->body.entry.attr.atimensec = 6; - out->body.entry.attr.mtimensec = 7; - out->body.entry.attr.ctimensec = 8; - out->body.entry.attr.nlink = 9; - out->body.entry.attr.uid = 10; - out->body.entry.attr.gid = 11; - out->body.entry.attr.rdev = 12; - out->body.entry.generation = generation; - }))); - /* stat(2) issues a VOP_LOOKUP followed by a VOP_GETATTR */ - ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); - EXPECT_EQ(1, sb.st_size); - EXPECT_EQ(2, sb.st_blocks); - EXPECT_EQ(3, sb.st_atim.tv_sec); - EXPECT_EQ(6, sb.st_atim.tv_nsec); - EXPECT_EQ(4, sb.st_mtim.tv_sec); - EXPECT_EQ(7, sb.st_mtim.tv_nsec); - EXPECT_EQ(5, sb.st_ctim.tv_sec); - EXPECT_EQ(8, sb.st_ctim.tv_nsec); - EXPECT_EQ(9ull, sb.st_nlink); - EXPECT_EQ(10ul, sb.st_uid); - EXPECT_EQ(11ul, sb.st_gid); - EXPECT_EQ(12ul, sb.st_rdev); - EXPECT_EQ(ino, sb.st_ino); - EXPECT_EQ(S_IFREG | 0644, sb.st_mode); - - // fuse(4) does not _yet_ support inode generations - //EXPECT_EQ(generation, sb.st_gen); - - //st_birthtim and st_flags are not supported by protocol 7.8. They're - //only supported as OS-specific extensions to OSX. - //EXPECT_EQ(, sb.st_birthtim); - //EXPECT_EQ(, sb.st_flags); - - //FUSE can't set st_blksize until protocol 7.9 -} - -/* - * If lookup returns a finite but non-zero cache timeout, then we should discard - * the cached attributes and requery the daemon. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ -TEST_F(Lookup, attr_cache_timeout) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - struct stat sb; - /* - * The timeout should be longer than the longest plausible time the - * daemon would take to complete a write(2) to /dev/fuse, but no longer. - */ - long timeout_ns = 250'000'000; - - EXPECT_LOOKUP(1, RELPATH) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.nodeid = ino; - out->body.entry.attr_valid_nsec = timeout_ns; - out->body.entry.attr.ino = ino; // Must match nodeid - out->body.entry.attr.mode = S_IFREG | 0644; - }))); - expect_getattr(ino, 0); - - /* access(2) will issue a VOP_LOOKUP but not a VOP_GETATTR */ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - usleep(2 * timeout_ns / 1000); - /* The cache has timed out; VOP_GETATTR should query the daemon*/ - ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); -} - -TEST_F(Lookup, enoent) -{ - const char FULLPATH[] = "mountpoint/does_not_exist"; - const char RELPATH[] = "does_not_exist"; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - EXPECT_NE(0, access(FULLPATH, F_OK)); - EXPECT_EQ(ENOENT, errno); -} - -/* - * If lookup returns a non-zero entry timeout, then subsequent VOP_LOOKUPs - * should use the cached inode rather than requery the daemon - */ -TEST_F(Lookup, entry_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.entry_valid = UINT64_MAX; - out->body.entry.attr.mode = S_IFREG | 0644; - out->body.entry.nodeid = 14; - }))); - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - /* The second access(2) should use the cache */ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); -} - -/* - * If the daemon returns an error of 0 and an inode of 0, that's a flag for - * "ENOENT and cache it" with the given entry_timeout - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236226 */ -TEST_F(Lookup, DISABLED_entry_cache_negative) -{ - struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; - - EXPECT_LOOKUP(1, "does_not_exist").Times(1) - .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))); - - EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK)); - EXPECT_EQ(ENOENT, errno); - EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK)); - EXPECT_EQ(ENOENT, errno); -} - -/* Negative entry caches should timeout, too */ -TEST_F(Lookup, entry_cache_negative_timeout) -{ - const char *RELPATH = "does_not_exist"; - const char *FULLPATH = "mountpoint/does_not_exist"; - /* - * The timeout should be longer than the longest plausible time the - * daemon would take to complete a write(2) to /dev/fuse, but no longer. - */ - struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 250'000'000}; - - EXPECT_LOOKUP(1, RELPATH).Times(2) - .WillRepeatedly(Invoke(ReturnNegativeCache(&entry_valid))); - - EXPECT_NE(0, access(FULLPATH, F_OK)); - EXPECT_EQ(ENOENT, errno); - - usleep(2 * entry_valid.tv_nsec / 1000); - - /* The cache has timed out; VOP_LOOKUP should requery the daemon*/ - EXPECT_NE(0, access(FULLPATH, F_OK)); - EXPECT_EQ(ENOENT, errno); -} - -/* - * If lookup returns a finite but non-zero entry cache timeout, then we should - * discard the cached inode and requery the daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ -TEST_F(Lookup, DISABLED_entry_cache_timeout) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - /* - * The timeout should be longer than the longest plausible time the - * daemon would take to complete a write(2) to /dev/fuse, but no longer. - */ - long timeout_ns = 250'000'000; - - EXPECT_LOOKUP(1, RELPATH).Times(2) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.entry_valid_nsec = timeout_ns; - out->body.entry.attr.mode = S_IFREG | 0644; - out->body.entry.nodeid = 14; - }))); - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); - usleep(2 * timeout_ns / 1000); - /* The cache has timed out; VOP_LOOKUP should query the daemon*/ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); -} - -// TODO: export_support -// After upgrading the protocol to 7.10, check that the kernel will only -// attempt to lookup "." and ".." if the filesystem sets FUSE_EXPORT_SUPPORT in -// the init flags. If not, then all lookups for those entries will return -// ESTALE. - -TEST_F(Lookup, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - - EXPECT_LOOKUP(1, RELPATH) - .WillOnce(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 = 14; - }))); - /* - * access(2) is one of the few syscalls that will not (always) follow - * up a successful VOP_LOOKUP with another VOP. - */ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); -} - -// Lookup in a subdirectory of the fuse mount -TEST_F(Lookup, subdir) -{ - const char FULLPATH[] = "mountpoint/some_dir/some_file.txt"; - const char DIRPATH[] = "some_dir"; - const char RELPATH[] = "some_file.txt"; - uint64_t dir_ino = 2; - uint64_t file_ino = 3; - - EXPECT_LOOKUP(1, DIRPATH) - .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFDIR | 0755; - out->body.entry.nodeid = dir_ino; - }))); - EXPECT_LOOKUP(dir_ino, RELPATH) - .WillOnce(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 = file_ino; - }))); - /* - * access(2) is one of the few syscalls that will not (always) follow - * up a successful VOP_LOOKUP with another VOP. - */ - ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); -} - - Property changes on: projects/fuse2/tests/sys/fs/fuse/lookup.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/interrupt.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/interrupt.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/interrupt.cc (nonexistent) @@ -1,312 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -/* Don't do anything; all we care about is that the syscall gets interrupted */ -void sigusr2_handler(int __unused sig) { - if (verbosity > 1) - printf("Signaled!\n"); -} - -void* killer(void* target) { - /* - * Sleep for awhile so we can be mostly confident that the main thread - * is already blocked in write(2) - */ - usleep(250'000); - if (verbosity > 1) - printf("Signalling!\n"); - pthread_kill(*(pthread_t*)target, SIGUSR2); - - return(NULL); -} - -class Interrupt: public FuseTest { -public: -pthread_t m_child; - -Interrupt(): m_child(NULL) {}; - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} - -/* - * Expect a FUSE_WRITE but don't reply. Instead, just record the unique value - * to the provided pointer - */ -void expect_write(uint64_t ino, uint64_t *write_unique) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_WRITE && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke([=](auto in, auto &out __unused) { - *write_unique = in->header.unique; - })); -} - -void setup_interruptor(pthread_t self) -{ - ASSERT_EQ(0, signal(SIGUSR2, sigusr2_handler)) << strerror(errno); - ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)self)) - << strerror(errno); -} - -void TearDown() { - if (m_child != NULL) { - pthread_join(m_child, NULL); - } - - FuseTest::TearDown(); -} -}; - -/* - * An interrupt operation that gets received after the original command is - * complete should generate an EAGAIN response. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ -TEST_F(Interrupt, DISABLED_already_complete) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - pthread_t self; - uint64_t write_unique = 0; - - self = pthread_self(); - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, &write_unique); - EXPECT_CALL(*m_mock, process( - ResultOf([&](auto in) { - return (in->header.opcode == FUSE_INTERRUPT && - in->body.interrupt.unique == write_unique); - }, Eq(true)), - _) - ).WillOnce(Invoke([&](auto in, auto &out) { - // First complete the write request - auto out0 = new mockfs_buf_out; - out0->header.unique = write_unique; - SET_OUT_HEADER_LEN(out0, write); - out0->body.write.size = bufsize; - out.push_back(out0); - - // Then, respond EAGAIN to the interrupt request - auto out1 = new mockfs_buf_out; - out1->header.unique = in->header.unique; - out1->header.error = -EAGAIN; - out1->header.len = sizeof(out1->header); - out.push_back(out1); - })); - - fd = open(FULLPATH, O_WRONLY); - ASSERT_LE(0, fd) << strerror(errno); - - setup_interruptor(self); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and - * complete the original operation whenever it damn well pleases. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ -TEST_F(Interrupt, DISABLED_ignore) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - pthread_t self; - uint64_t write_unique; - - self = pthread_self(); - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, &write_unique); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_INTERRUPT && - in->body.interrupt.unique == write_unique); - }, Eq(true)), - _) - ).WillOnce(Invoke([&](auto in __unused, auto &out) { - // Ignore FUSE_INTERRUPT; respond to the FUSE_WRITE - auto out0 = new mockfs_buf_out; - out0->header.unique = write_unique; - SET_OUT_HEADER_LEN(out0, write); - out0->body.write.size = bufsize; - out.push_back(out0); - })); - - fd = open(FULLPATH, O_WRONLY); - ASSERT_LE(0, fd) << strerror(errno); - - setup_interruptor(self); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * A syscall that gets interrupted while blocking on FUSE I/O should send a - * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR - * in response to the _original_ operation. The kernel should ultimately - * return EINTR to userspace - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ -TEST_F(Interrupt, DISABLED_in_progress) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - pthread_t self; - uint64_t write_unique; - - self = pthread_self(); - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, &write_unique); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_INTERRUPT && - in->body.interrupt.unique == write_unique); - }, Eq(true)), - _) - ).WillOnce(Invoke([&](auto in __unused, auto &out) { - auto out0 = new mockfs_buf_out; - out0->header.error = -EINTR; - out0->header.unique = write_unique; - out0->header.len = sizeof(out0->header); - out.push_back(out0); - })); - - fd = open(FULLPATH, O_WRONLY); - ASSERT_LE(0, fd) << strerror(errno); - - setup_interruptor(self); - ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); - EXPECT_EQ(EINTR, errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the FUSE filesystem receives the FUSE_INTERRUPT operation before - * processing the original, then it should wait for "some timeout" for the - * original operation to arrive. If not, it should send EAGAIN to the - * INTERRUPT operation, and the kernel should requeue the INTERRUPT. - * - * In this test, we'll pretend that the INTERRUPT arrives too soon, gets - * EAGAINed, then the kernel requeues it, and the second time around it - * successfully interrupts the original - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ -TEST_F(Interrupt, DISABLED_too_soon) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - pthread_t self; - uint64_t write_unique; - - self = pthread_self(); - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, &write_unique); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_INTERRUPT && - in->body.interrupt.unique == write_unique); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EAGAIN))) - .RetiresOnSaturation(); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_INTERRUPT && - in->body.interrupt.unique == write_unique); - }, Eq(true)), - _) - ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { - auto out0 = new mockfs_buf_out; - out0->header.error = -EINTR; - out0->header.unique = write_unique; - out0->header.len = sizeof(out0->header); - out.push_back(out0); - })); - - fd = open(FULLPATH, O_WRONLY); - ASSERT_LE(0, fd) << strerror(errno); - - setup_interruptor(self); - ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); - EXPECT_EQ(EINTR, errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/interrupt.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/getattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/getattr.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/getattr.cc (nonexistent) @@ -1,185 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Getattr : public FuseTest {}; - -/* - * If getattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs - * should use the cached attributes, rather than query the daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ -TEST_F(Getattr, DISABLED_attr_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - struct stat sb; - - EXPECT_LOOKUP(1, RELPATH) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, entry); - out->body.entry.attr.mode = S_IFREG | 0644; - out->body.entry.nodeid = ino; - }))); - 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_valid = UINT64_MAX; - out->body.attr.attr.ino = ino; // Must match nodeid - out->body.attr.attr.mode = S_IFREG | 0644; - }))); - EXPECT_EQ(0, stat(FULLPATH, &sb)); - /* The second stat(2) should use cached attributes */ - EXPECT_EQ(0, stat(FULLPATH, &sb)); -} - -/* - * If getattr returns a finite but non-zero cache timeout, then we should - * discard the cached attributes and requery the daemon after the timeout - * period passes. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ -TEST_F(Getattr, attr_cache_timeout) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - struct stat sb; - /* - * The timeout should be longer than the longest plausible time the - * daemon would take to complete a write(2) to /dev/fuse, but no longer. - */ - long timeout_ns = 250'000'000; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).Times(2) - .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, attr); - out->body.attr.attr_valid_nsec = timeout_ns; - out->body.attr.attr_valid = UINT64_MAX; - out->body.attr.attr.ino = ino; // Must match nodeid - out->body.attr.attr.mode = S_IFREG | 0644; - }))); - EXPECT_EQ(0, stat(FULLPATH, &sb)); - usleep(2 * timeout_ns / 1000); - /* Timeout has expire. stat(2) should requery the daemon */ - EXPECT_EQ(0, stat(FULLPATH, &sb)); -} - -TEST_F(Getattr, enoent) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - struct stat sb; - const uint64_t ino = 42; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); - EXPECT_CALL(*m_mock, process( - ResultOf([](auto in) { - return (in->header.opcode == FUSE_GETATTR && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(ENOENT))); - EXPECT_NE(0, stat(FULLPATH, &sb)); - EXPECT_EQ(ENOENT, errno); -} - -TEST_F(Getattr, ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const uint64_t ino = 42; - struct stat sb; - - expect_lookup(RELPATH, ino, S_IFREG | 0644, 1, 1); - 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 = S_IFREG | 0644; - out->body.attr.attr.size = 1; - out->body.attr.attr.blocks = 2; - out->body.attr.attr.atime = 3; - out->body.attr.attr.mtime = 4; - out->body.attr.attr.ctime = 5; - out->body.attr.attr.atimensec = 6; - out->body.attr.attr.mtimensec = 7; - out->body.attr.attr.ctimensec = 8; - out->body.attr.attr.nlink = 9; - out->body.attr.attr.uid = 10; - out->body.attr.attr.gid = 11; - out->body.attr.attr.rdev = 12; - }))); - - ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); - EXPECT_EQ(1, sb.st_size); - EXPECT_EQ(2, sb.st_blocks); - EXPECT_EQ(3, sb.st_atim.tv_sec); - EXPECT_EQ(6, sb.st_atim.tv_nsec); - EXPECT_EQ(4, sb.st_mtim.tv_sec); - EXPECT_EQ(7, sb.st_mtim.tv_nsec); - EXPECT_EQ(5, sb.st_ctim.tv_sec); - EXPECT_EQ(8, sb.st_ctim.tv_nsec); - EXPECT_EQ(9ull, sb.st_nlink); - EXPECT_EQ(10ul, sb.st_uid); - EXPECT_EQ(11ul, sb.st_gid); - EXPECT_EQ(12ul, sb.st_rdev); - EXPECT_EQ(ino, sb.st_ino); - EXPECT_EQ(S_IFREG | 0644, sb.st_mode); - - //st_birthtim and st_flags are not supported by protocol 7.8. They're - //only supported as OS-specific extensions to OSX. - //EXPECT_EQ(, sb.st_birthtim); - //EXPECT_EQ(, sb.st_flags); - - //FUSE can't set st_blksize until protocol 7.9 -} Property changes on: projects/fuse2/tests/sys/fs/fuse/getattr.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/fsync.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/fsync.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/fsync.cc (nonexistent) @@ -1,313 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -/* - * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28. - * This bit was actually part of kernel protocol version 5.2, but never - * documented until after 7.28 - */ -#ifndef FUSE_FSYNC_FDATASYNC -#define FUSE_FSYNC_FDATASYNC 1 -#endif - -class Fsync: public FuseTest { -public: -void expect_fsync(uint64_t ino, uint32_t flags, int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_FSYNC && - in->header.nodeid == ino && - in->body.fsync.fh == FH && - in->body.fsync.fsync_flags == flags); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} - -void expect_write(uint64_t ino, uint64_t size, const void *contents) -{ - FuseTest::expect_write(ino, 0, size, size, 0, contents); -} - -}; - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(Fsync, DISABLED_aio_fsync) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - struct aiocb iocb, *piocb; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - expect_fsync(ino, 0, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - - bzero(&iocb, sizeof(iocb)); - iocb.aio_fildes = fd; - - ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno); - ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE - * - * This test only really make sense in writeback caching mode, but it should - * still pass in any cache mode. - */ -TEST_F(Fsync, close) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETATTR); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { - SET_OUT_HEADER_LEN(out, attr); - out->body.attr.attr.ino = ino; // Must match nodeid - }))); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_FSYNC); - }, Eq(true)), - _) - ).Times(0); - expect_release(ino, 1, 0, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - close(fd); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(Fsync, DISABLED_eio) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - ASSERT_NE(0, fdatasync(fd)); - ASSERT_EQ(EIO, errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * If the filesystem returns ENOSYS, it will be treated as success and - * subsequent calls to VOP_FSYNC will succeed automatically without being sent - * to the filesystem daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Fsync, DISABLED_enosys) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - EXPECT_EQ(0, fdatasync(fd)); - - /* Subsequent calls shouldn't query the daemon*/ - EXPECT_EQ(0, fdatasync(fd)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(Fsync, DISABLED_fdatasync) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - ASSERT_EQ(0, fdatasync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(Fsync, DISABLED_fsync) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - ssize_t bufsize = strlen(CONTENTS); - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_write(ino, bufsize, CONTENTS); - expect_fsync(ino, 0, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); - ASSERT_EQ(0, fsync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Fsync should sync a file with dirty metadata but clean data */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ -TEST_F(Fsync, DISABLED_fsync_metadata_only) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - mode_t mode = 0755; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETATTR); - }, 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 = S_IFREG | mode; - }))); - - expect_fsync(ino, 0, 0); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - ASSERT_EQ(0, fchmod(fd, mode)) << strerror(errno); - ASSERT_EQ(0, fsync(fd)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -// fsync()ing a file that isn't dirty should be a no-op -TEST_F(Fsync, nop) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - - fd = open(FULLPATH, O_WRONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(0, fsync(fd)) << strerror(errno); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -// TODO: ENOSYS test Property changes on: projects/fuse2/tests/sys/fs/fuse/fsync.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/read.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/read.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/read.cc (nonexistent) @@ -1,739 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -#include -#include -#include - -#include -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Read: public FuseTest { - -public: -void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); -} -}; - -class AioRead: public Read { -public: -virtual void SetUp() { - const char *node = "vfs.aio.enable_unsafe"; - int val = 0; - size_t size = sizeof(val); - - FuseTest::SetUp(); - - ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) - << strerror(errno); - if (!val) - GTEST_SKIP() << - "vfs.aio.enable_unsafe must be set for this test"; -} -}; - -class AsyncRead: public AioRead { - virtual void SetUp() { - m_init_flags = FUSE_ASYNC_READ; - AioRead::SetUp(); - } -}; - -class ReadAhead: public Read, public WithParamInterface { - virtual void SetUp() { - m_maxreadahead = GetParam(); - Read::SetUp(); - } -}; - -/* AIO reads need to set the header's pid field correctly */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ -TEST_F(AioRead, aio_read) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - struct aiocb iocb, *piocb; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - iocb.aio_nbytes = bufsize; - iocb.aio_fildes = fd; - iocb.aio_buf = buf; - iocb.aio_offset = 0; - iocb.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno); - ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there - * is at most one outstanding read operation per file handle - */ -TEST_F(AioRead, async_read_disabled) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = 50; - char buf0[bufsize], buf1[bufsize]; - off_t off0 = 0; - off_t off1 = 4096; - struct aiocb iocb0, iocb1; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == FH && - in->body.read.offset == (uint64_t)off0 && - in->body.read.size == bufsize); - }, Eq(true)), - _) - ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { - /* Filesystem is slow to respond */ - })); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == FH && - in->body.read.offset == (uint64_t)off1 && - in->body.read.size == bufsize); - }, Eq(true)), - _) - ).Times(0); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - /* - * Submit two AIO read requests, and respond to neither. If the - * filesystem ever gets the second read request, then we failed to - * limit outstanding reads. - */ - iocb0.aio_nbytes = bufsize; - iocb0.aio_fildes = fd; - iocb0.aio_buf = buf0; - iocb0.aio_offset = off0; - iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); - - iocb1.aio_nbytes = bufsize; - iocb1.aio_fildes = fd; - iocb1.aio_buf = buf1; - iocb1.aio_offset = off1; - iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); - - /* - * Sleep for awhile to make sure the kernel has had a chance to issue - * the second read, even though the first has not yet returned - */ - usleep(250'000); - - /* Deliberately leak iocbs */ - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple - * simultaneous read requests on the same file handle. - */ -/* - * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla - * entry, because that's a feature request, not a bug. - */ -TEST_F(AsyncRead, DISABLED_async_read) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = 50; - char buf0[bufsize], buf1[bufsize]; - off_t off0 = 0; - off_t off1 = 4096; - struct aiocb iocb0, iocb1; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == FH && - in->body.read.offset == (uint64_t)off0 && - in->body.read.size == bufsize); - }, Eq(true)), - _) - ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { - /* Filesystem is slow to respond */ - })); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == FH && - in->body.read.offset == (uint64_t)off1 && - in->body.read.size == bufsize); - }, Eq(true)), - _) - ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { - /* Filesystem is slow to respond */ - })); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - /* - * Submit two AIO read requests, but respond to neither. Ensure that - * we received both. - */ - iocb0.aio_nbytes = bufsize; - iocb0.aio_fildes = fd; - iocb0.aio_buf = buf0; - iocb0.aio_offset = off0; - iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); - - iocb1.aio_nbytes = bufsize; - iocb1.aio_fildes = fd; - iocb1.aio_buf = buf1; - iocb1.aio_offset = off1; - iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; - ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); - - /* - * Sleep for awhile to make sure the kernel has had a chance to issue - * both reads. - */ - usleep(250'000); - - /* Deliberately leak iocbs */ - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* 0-length reads shouldn't cause any confusion */ -TEST_F(Read, direct_io_read_nothing) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - uint64_t offset = 100; - char buf[80]; - - expect_lookup(RELPATH, ino, offset + 1000); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, offset + 1000); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * With direct_io, reads should not fill the cache. They should go straight to - * the daemon - */ -TEST_F(Read, direct_io_pread) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - uint64_t offset = 100; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - expect_lookup(RELPATH, ino, offset + bufsize); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, offset + bufsize); - expect_read(ino, offset, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * With direct_io, filesystems are allowed to return less data than is - * requested. fuse(4) should return a short read to userland. - */ -TEST_F(Read, direct_io_short_read) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefghijklmnop"; - uint64_t ino = 42; - int fd; - uint64_t offset = 100; - ssize_t bufsize = strlen(CONTENTS); - ssize_t halfbufsize = bufsize / 2; - char buf[bufsize]; - - expect_lookup(RELPATH, ino, offset + bufsize); - expect_open(ino, FOPEN_DIRECT_IO, 1); - expect_getattr(ino, offset + bufsize); - expect_read(ino, offset, bufsize, halfbufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset)) - << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -TEST_F(Read, eio) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EIO))); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(EIO, errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * With the keep_cache option, the kernel may keep its read cache across - * multiple open(2)s. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236560 */ -TEST_F(Read, DISABLED_keep_cache) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd0, fd1; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); - expect_open(ino, FOPEN_KEEP_CACHE, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd0 = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd0) << strerror(errno); - ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); - - fd1 = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd1) << strerror(errno); - - /* - * This read should be serviced by cache, even though it's on the other - * file descriptor - */ - ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno); - - /* Deliberately leak fd0 and fd1. */ -} - -/* - * Without the keep_cache option, the kernel should drop its read caches on - * every open - */ -TEST_F(Read, keep_cache_disabled) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd0, fd1; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); - expect_open(ino, FOPEN_KEEP_CACHE, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd0 = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd0) << strerror(errno); - ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); - - fd1 = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd1) << strerror(errno); - - /* - * This read should not be serviced by cache, even though it's on the - * original file descriptor - */ - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); - - /* Deliberately leak fd0 and fd1. */ -} - -TEST_F(Read, mmap) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t len; - ssize_t bufsize = strlen(CONTENTS); - void *p; - //char buf[bufsize]; - - len = getpagesize(); - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - /* mmap may legitimately try to read more data than is available */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == Read::FH && - in->body.read.offset == 0 && - in->body.read.size >= bufsize); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(struct fuse_out_header) + bufsize; - memmove(out->body.bytes, CONTENTS, bufsize); - }))); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); - ASSERT_NE(MAP_FAILED, p) << strerror(errno); - - ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); - - ASSERT_EQ(0, munmap(p, len)) << strerror(errno); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* - * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass - * cache and to straight to the daemon - */ -TEST_F(Read, o_direct) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - // Fill the cache - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - - // Reads with o_direct should bypass the cache - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); - ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -TEST_F(Read, pread) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - /* - * Set offset to a maxbcachebuf boundary so we'll be sure what offset - * to read from. Without this, the read might start at a lower offset. - */ - uint64_t offset = m_maxbcachebuf; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - expect_lookup(RELPATH, ino, offset + bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, offset + bufsize); - expect_read(ino, offset, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -TEST_F(Read, read) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - expect_read(ino, 0, bufsize, bufsize, CONTENTS); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* If the filesystem allows it, the kernel should try to readahead */ -TEST_F(Read, default_readahead) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS0 = "abcdefghijklmnop"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = 8; - /* hard-coded in fuse_internal.c */ - size_t default_maxreadahead = 65536; - ssize_t filesize = default_maxreadahead * 2; - char *contents; - char buf[bufsize]; - const char *contents1 = CONTENTS0 + bufsize; - - contents = (char*)calloc(1, filesize); - ASSERT_NE(NULL, contents); - memmove(contents, CONTENTS0, strlen(CONTENTS0)); - - expect_lookup(RELPATH, ino, filesize); - expect_open(ino, 0, 1); - expect_getattr(ino, filesize); - expect_read(ino, 0, default_maxreadahead, default_maxreadahead, - contents); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); - - /* A subsequent read should be serviced by cache */ - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, contents1, bufsize)); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* Reading with sendfile should work (though it obviously won't be 0-copy) */ -TEST_F(Read, sendfile) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - char buf[bufsize]; - int sp[2]; - off_t sbytes; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - /* Like mmap, sendfile may request more data than is available */ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ && - in->header.nodeid == ino && - in->body.read.fh == Read::FH && - in->body.read.offset == 0 && - in->body.read.size >= bufsize); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.len = sizeof(struct fuse_out_header) + bufsize; - memmove(out->body.bytes, CONTENTS, bufsize); - }))); - - ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) - << strerror(errno); - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)) - << strerror(errno); - ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); - - close(sp[1]); - close(sp[0]); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* sendfile should fail gracefully if fuse declines the read */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */ -TEST_F(Read, DISABLED_sendfile_eio) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS = "abcdefgh"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = strlen(CONTENTS); - int sp[2]; - off_t sbytes; - - expect_lookup(RELPATH, ino, bufsize); - expect_open(ino, 0, 1); - expect_getattr(ino, bufsize); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READ); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EIO))); - - ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) - << strerror(errno); - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)); - - close(sp[1]); - close(sp[0]); - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -/* fuse(4) should honor the filesystem's requested m_readahead parameter */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */ -TEST_P(ReadAhead, DISABLED_readahead) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - const char *CONTENTS0 = "abcdefghijklmnop"; - uint64_t ino = 42; - int fd; - ssize_t bufsize = 8; - ssize_t filesize = m_maxbcachebuf * 2; - char *contents; - char buf[bufsize]; - - ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf) - << "Test assumes that max_readahead < maxbcachebuf"; - - contents = (char*)calloc(1, filesize); - ASSERT_NE(NULL, contents); - memmove(contents, CONTENTS0, strlen(CONTENTS0)); - - expect_lookup(RELPATH, ino, filesize); - expect_open(ino, 0, 1); - expect_getattr(ino, filesize); - /* fuse(4) should only read ahead the allowed amount */ - expect_read(ino, 0, GetParam(), GetParam(), contents); - - fd = open(FULLPATH, O_RDONLY); - ASSERT_LE(0, fd) << strerror(errno); - - ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); - ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); - - /* Deliberately leak fd. close(2) will be tested in release.cc */ -} - -INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u)); Property changes on: projects/fuse2/tests/sys/fs/fuse/read.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/mknod.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/mknod.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/mknod.cc (nonexistent) @@ -1,138 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Mknod: public FuseTest { - -public: - -virtual void SetUp() { - FuseTest::SetUp(); - - if (geteuid() != 0) { - GTEST_SKIP() << "Only root may use most mknod(2) variations"; - } -} - -/* Test an OK creation of a file with the given mode and device number */ -void test_ok(mode_t mode, dev_t dev) { - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_mknod_in); - return (in->header.opcode == FUSE_MKNOD && - in->body.mknod.mode == mode && - in->body.mknod.rdev == dev && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - SET_OUT_HEADER_LEN(out, create); - out->body.create.entry.attr.mode = mode; - out->body.create.entry.nodeid = ino; - out->body.create.entry.entry_valid = UINT64_MAX; - out->body.create.entry.attr_valid = UINT64_MAX; - out->body.create.entry.attr.rdev = dev; - }))); - EXPECT_EQ(0, mknod(FULLPATH, mode, dev)) << strerror(errno); -} - -}; - -/* - * mknod(2) should be able to create block devices on a FUSE filesystem. Even - * though FreeBSD doesn't use block devices, this is useful when copying media - * from or preparing media for other operating systems. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_blk) -{ - test_ok(S_IFBLK | 0755, 0xfe00); /* /dev/vda's device number on Linux */ -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_chr) -{ - test_ok(S_IFCHR | 0755, 0x64); /* /dev/fuse's device number */ -} - -/* - * The daemon is responsible for checking file permissions (unless the - * default_permissions mount option was used) - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_eperm) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - mode_t mode = S_IFIFO | 0755; - - EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); - - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - const char *name = (const char*)in->body.bytes + - sizeof(fuse_mknod_in); - return (in->header.opcode == FUSE_MKNOD && - in->body.mknod.mode == mode && - (0 == strcmp(RELPATH, name))); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(EPERM))); - EXPECT_NE(0, mknod(FULLPATH, mode, 0)); - EXPECT_EQ(EPERM, errno); -} - - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_fifo) -{ - test_ok(S_IFIFO | 0755, 0); -} - -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ -TEST_F(Mknod, DISABLED_whiteout) -{ - test_ok(S_IFWHT | 0755, 0); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/mknod.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/flush.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/flush.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/flush.cc (nonexistent) @@ -1,231 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Flush: public FuseTest { - -public: -void expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_FLUSH && - in->header.nodeid == ino && - in->body.flush.lock_owner == (uint64_t)lo && - in->body.flush.fh == FH); - }, Eq(true)), - _) - ).Times(times) - .WillRepeatedly(Invoke(r)); -} - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} - -/* - * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting. This - * expectation will silence googlemock warnings - */ -void expect_release() -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_RELEASE); - }, Eq(true)), - _) - ).WillRepeatedly(Invoke(ReturnErrno(0))); -} -}; - -class FlushWithLocks: public Flush { - virtual void SetUp() { - m_init_flags = FUSE_POSIX_LOCKS; - Flush::SetUp(); - } -}; - -/* If a file descriptor is duplicated, every close causes FLUSH */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ -TEST_F(Flush, DISABLED_dup) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd, fd2; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_flush(ino, 2, 0, ReturnErrno(0)); - expect_release(); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - fd2 = dup(fd); - - ASSERT_EQ(0, close(fd2)) << strerror(errno); - ASSERT_EQ(0, close(fd)) << strerror(errno); -} - -/* - * Some FUSE filesystem cache data internally and flush it on release. Such - * filesystems may generate errors during release. On Linux, these get - * returned by close(2). However, POSIX does not require close(2) to return - * this error. FreeBSD's fuse(4) should return EIO if it returns an error at - * all. - */ -/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ -TEST_F(Flush, DISABLED_eio) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_flush(ino, 1, 0, ReturnErrno(EIO)); - expect_release(); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); -} - -/* - * If the filesystem returns ENOSYS, it will be treated as success and - * no more FUSE_FLUSH operations will be sent to the daemon - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -TEST_F(Flush, DISABLED_enosys) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd, fd2; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - /* On the 2nd close, FUSE_FLUSH won't be sent at all */ - expect_flush(ino, 1, 0, ReturnErrno(ENOSYS)); - expect_release(); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - fd2 = dup(fd); - - EXPECT_EQ(0, close(fd2)) << strerror(errno); - EXPECT_EQ(0, close(fd)) << strerror(errno); -} - -/* A FUSE_FLUSH should be sent on close(2) */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ -TEST_F(Flush, DISABLED_flush) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd; - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - expect_flush(ino, 1, 0, ReturnErrno(0)); - expect_release(); - - fd = open(FULLPATH, O_WRONLY); - EXPECT_LE(0, fd) << strerror(errno); - - ASSERT_TRUE(0 == close(fd)) << strerror(errno); -} - -/* - * When closing a file with a POSIX file lock, flush should release the lock, - * _even_if_ it's not the process's last file descriptor for this file. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ -TEST_F(FlushWithLocks, DISABLED_unlock_on_close) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - int fd, fd2; - struct flock fl; - pid_t pid = getpid(); - - expect_lookup(RELPATH, ino); - expect_open(ino, 0, 1); - expect_getattr(ino, 0); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_SETLK && - in->header.nodeid == ino && - in->body.setlk.fh == FH); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { - SET_OUT_HEADER_LEN(out, setlk); - out->body.setlk.lk = in->body.setlk.lk; - }))); - expect_flush(ino, 1, pid, ReturnErrno(0)); - - fd = open(FULLPATH, O_RDWR); - ASSERT_LE(0, fd) << strerror(errno); - fl.l_start = 0; - fl.l_len = 0; - fl.l_pid = pid; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_sysid = 0; - ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); - - fd2 = dup(fd); - ASSERT_EQ(0, close(fd2)) << strerror(errno); - /* Deliberately leak fd */ -} Property changes on: projects/fuse2/tests/sys/fs/fuse/flush.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/readlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/readlink.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/readlink.cc (nonexistent) @@ -1,122 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include - -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Readlink: public FuseTest { -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 0, 1); -} -void expect_readlink(uint64_t ino, ProcessMockerT r) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READLINK && - in->header.nodeid == ino); - }, Eq(true)), - _) - ).WillOnce(Invoke(r)); -} - -}; - -class PushSymlinksIn: public Readlink { - virtual void SetUp() { - m_push_symlinks_in = true; - Readlink::SetUp(); - } -}; - -TEST_F(Readlink, eloop) -{ - const char FULLPATH[] = "mountpoint/src"; - const char RELPATH[] = "src"; - const uint64_t ino = 42; - char buf[80]; - - expect_lookup(RELPATH, ino); - expect_readlink(ino, ReturnErrno(ELOOP)); - - EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf))); - EXPECT_EQ(ELOOP, errno); -} - -TEST_F(Readlink, ok) -{ - const char FULLPATH[] = "mountpoint/src"; - const char RELPATH[] = "src"; - const char dst[] = "dst"; - const uint64_t ino = 42; - char buf[80]; - - expect_lookup(RELPATH, ino); - expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) { - strlcpy(out->body.str, dst, sizeof(out->body.str)); - out->header.len = sizeof(out->header) + strlen(dst) + 1; - })); - - EXPECT_EQ((ssize_t)strlen(dst) + 1, - readlink(FULLPATH, buf, sizeof(buf))); - EXPECT_STREQ(dst, buf); -} - -TEST_F(PushSymlinksIn, readlink) -{ - const char FULLPATH[] = "mountpoint/src"; - const char RELPATH[] = "src"; - const char dst[] = "/dst"; - const uint64_t ino = 42; - char buf[MAXPATHLEN], wd[MAXPATHLEN], want[MAXPATHLEN]; - int len; - - expect_lookup(RELPATH, ino); - expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) { - strlcpy(out->body.str, dst, sizeof(out->body.str)); - out->header.len = sizeof(out->header) + strlen(dst) + 1; - })); - - ASSERT_NE(NULL, getcwd(wd, sizeof(wd))) << strerror(errno); - len = snprintf(want, sizeof(want), "%s/mountpoint%s", wd, dst); - ASSERT_LE(0, len) << strerror(errno); - - EXPECT_EQ((ssize_t)len + 1, readlink(FULLPATH, buf, sizeof(buf))); - EXPECT_STREQ(want, buf); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/readlink.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/releasedir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/releasedir.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/releasedir.cc (nonexistent) @@ -1,109 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class ReleaseDir: public FuseTest { - -public: -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); -} - -void expect_releasedir(uint64_t ino, ProcessMockerT r) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_RELEASEDIR && - in->header.nodeid == ino && - in->body.release.fh == FH); - }, Eq(true)), - _) - ).WillOnce(Invoke(r)); -} -}; - -/* If a file descriptor is duplicated, only the last close causes RELEASE */ -TEST_F(ReleaseDir, dup) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - DIR *dir, *dir2; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_READDIR && - in->header.nodeid == ino && - in->body.readdir.offset == 0); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { - out->header.error = 0; - out->header.len = sizeof(out->header); - }))); - expect_releasedir(ino, ReturnErrno(0)); - - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - - dir2 = fdopendir(dup(dirfd(dir))); - ASSERT_NE(NULL, dir2) << strerror(errno); - - ASSERT_EQ(0, closedir(dir)) << strerror(errno); - ASSERT_EQ(0, closedir(dir2)) << strerror(errno); -} - -TEST_F(ReleaseDir, ok) -{ - const char FULLPATH[] = "mountpoint/some_dir"; - const char RELPATH[] = "some_dir"; - uint64_t ino = 42; - DIR *dir; - - expect_lookup(RELPATH, ino); - expect_opendir(ino); - expect_releasedir(ino, ReturnErrno(0)); - - dir = opendir(FULLPATH); - ASSERT_NE(NULL, dir) << strerror(errno); - - ASSERT_EQ(0, closedir(dir)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/releasedir.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fuse/access.cc =================================================================== --- projects/fuse2/tests/sys/fs/fuse/access.cc (revision 345355) +++ projects/fuse2/tests/sys/fs/fuse/access.cc (nonexistent) @@ -1,111 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2019 The FreeBSD Foundation - * - * This software was developed by BFF Storage Systems, LLC under sponsorship - * from the FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -extern "C" { -#include -#include -} - -#include "mockfs.hh" -#include "utils.hh" - -using namespace testing; - -class Access: public FuseTest { -public: -void expect_access(uint64_t ino, mode_t access_mode, int error) -{ - EXPECT_CALL(*m_mock, process( - ResultOf([=](auto in) { - return (in->header.opcode == FUSE_ACCESS && - in->header.nodeid == ino && - in->body.access.mask == access_mode); - }, Eq(true)), - _) - ).WillOnce(Invoke(ReturnErrno(error))); -} - -void expect_lookup(const char *relpath, uint64_t ino) -{ - FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); -} -}; - -/* The error case of FUSE_ACCESS. */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ -TEST_F(Access, DISABLED_eaccess) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - mode_t access_mode = X_OK; - - expect_lookup(RELPATH, ino); - expect_access(ino, access_mode, EACCES); - - ASSERT_NE(0, access(FULLPATH, access_mode)); - ASSERT_EQ(EACCES, errno); -} - -/* - * If the filesystem returns ENOSYS, then it is treated as a permanent success, - * and subsequent VOP_ACCESS calls will succeed automatically without querying - * the daemon. - */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ -TEST_F(Access, DISABLED_enosys) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - mode_t access_mode = R_OK; - - expect_lookup(RELPATH, ino); - expect_access(ino, access_mode, ENOSYS); - - ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); - ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); -} - -/* The successful case of FUSE_ACCESS. */ -/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ -TEST_F(Access, DISABLED_ok) -{ - const char FULLPATH[] = "mountpoint/some_file.txt"; - const char RELPATH[] = "some_file.txt"; - uint64_t ino = 42; - mode_t access_mode = R_OK; - - expect_lookup(RELPATH, ino); - expect_access(ino, access_mode, 0); - - ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); -} Property changes on: projects/fuse2/tests/sys/fs/fuse/access.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/Makefile =================================================================== --- projects/fuse2/tests/sys/fs/Makefile (revision 345355) +++ projects/fuse2/tests/sys/fs/Makefile (revision 345356) @@ -1,24 +1,24 @@ # $FreeBSD$ PACKAGE= tests TESTSDIR= ${TESTSBASE}/sys/fs TESTSRC= ${SRCTOP}/contrib/netbsd-tests/fs #TESTS_SUBDIRS+= nullfs # XXX: needs rump -TESTS_SUBDIRS+= fuse +TESTS_SUBDIRS+= fusefs TESTS_SUBDIRS+= tmpfs ${PACKAGE}FILES+= h_funcs.subr ${PACKAGE}FILESDIR= ${TESTSDIR} CLEANFILES+= h_funcs.subr CLEANFILES+= h_funcs.subr.tmp h_funcs.subr: ${TESTSRC}/h_funcs.subr cat ${.ALLSRC} | \ sed -e '/atf_require_prog mount_$${name}/d' >>${.TARGET}.tmp mv ${.TARGET}.tmp ${.TARGET} .include Index: projects/fuse2/tests/sys/fs/fusefs/Makefile =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/Makefile (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/Makefile (revision 345356) @@ -0,0 +1,208 @@ +# $FreeBSD$ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/fs/fusefs + +# We could simply link all of these files into a single executable. But since +# Kyua treats googletest programs as plain tests, it's better to separate them +# out, so we get more granular reporting. +GTESTS+= access +GTESTS+= create +GTESTS+= default_permissions +GTESTS+= destroy +GTESTS+= flush +GTESTS+= fsync +GTESTS+= fsyncdir +GTESTS+= getattr +GTESTS+= interrupt +GTESTS+= link +GTESTS+= locks +GTESTS+= lookup +GTESTS+= mkdir +GTESTS+= mknod +GTESTS+= open +GTESTS+= opendir +GTESTS+= read +GTESTS+= readdir +GTESTS+= readlink +GTESTS+= release +GTESTS+= releasedir +GTESTS+= rename +GTESTS+= rmdir +GTESTS+= setattr +GTESTS+= statfs +GTESTS+= symlink +GTESTS+= unlink +GTESTS+= write +GTESTS+= xattr + +SRCS.access+= access.cc +SRCS.access+= getmntopts.c +SRCS.access+= mockfs.cc +SRCS.access+= utils.cc + +SRCS.create+= create.cc +SRCS.create+= getmntopts.c +SRCS.create+= mockfs.cc +SRCS.create+= utils.cc + +SRCS.default_permissions+= default_permissions.cc +SRCS.default_permissions+= getmntopts.c +SRCS.default_permissions+= mockfs.cc +SRCS.default_permissions+= utils.cc +TEST_METADATA.default_permissions+= required_user="unprivileged" + +SRCS.destroy+= destroy.cc +SRCS.destroy+= getmntopts.c +SRCS.destroy+= mockfs.cc +SRCS.destroy+= utils.cc + +SRCS.flush+= flush.cc +SRCS.flush+= getmntopts.c +SRCS.flush+= mockfs.cc +SRCS.flush+= utils.cc + +SRCS.fsync+= fsync.cc +SRCS.fsync+= getmntopts.c +SRCS.fsync+= mockfs.cc +SRCS.fsync+= utils.cc + +SRCS.fsyncdir+= fsyncdir.cc +SRCS.fsyncdir+= getmntopts.c +SRCS.fsyncdir+= mockfs.cc +SRCS.fsyncdir+= utils.cc + +SRCS.getattr+= getattr.cc +SRCS.getattr+= getmntopts.c +SRCS.getattr+= mockfs.cc +SRCS.getattr+= utils.cc + +SRCS.interrupt+= interrupt.cc +SRCS.interrupt+= getmntopts.c +SRCS.interrupt+= mockfs.cc +SRCS.interrupt+= utils.cc + +SRCS.link+= getmntopts.c +SRCS.link+= link.cc +SRCS.link+= mockfs.cc +SRCS.link+= utils.cc + +SRCS.locks+= locks.cc +SRCS.locks+= getmntopts.c +SRCS.locks+= mockfs.cc +SRCS.locks+= utils.cc + +SRCS.lookup+= getmntopts.c +SRCS.lookup+= lookup.cc +SRCS.lookup+= mockfs.cc +SRCS.lookup+= utils.cc + +SRCS.mkdir+= getmntopts.c +SRCS.mkdir+= mockfs.cc +SRCS.mkdir+= mkdir.cc +SRCS.mkdir+= utils.cc + +SRCS.mknod+= getmntopts.c +SRCS.mknod+= mockfs.cc +SRCS.mknod+= mknod.cc +SRCS.mknod+= utils.cc +TEST_METADATA.mknod+= required_user="root" + +SRCS.open+= getmntopts.c +SRCS.open+= mockfs.cc +SRCS.open+= open.cc +SRCS.open+= utils.cc + +SRCS.opendir+= getmntopts.c +SRCS.opendir+= mockfs.cc +SRCS.opendir+= opendir.cc +SRCS.opendir+= utils.cc + +SRCS.read+= getmntopts.c +SRCS.read+= mockfs.cc +SRCS.read+= read.cc +SRCS.read+= utils.cc + +SRCS.readdir+= getmntopts.c +SRCS.readdir+= mockfs.cc +SRCS.readdir+= readdir.cc +SRCS.readdir+= utils.cc + +SRCS.readlink+= getmntopts.c +SRCS.readlink+= mockfs.cc +SRCS.readlink+= readlink.cc +SRCS.readlink+= utils.cc + +SRCS.release+= getmntopts.c +SRCS.release+= mockfs.cc +SRCS.release+= release.cc +SRCS.release+= utils.cc + +SRCS.releasedir+= getmntopts.c +SRCS.releasedir+= mockfs.cc +SRCS.releasedir+= releasedir.cc +SRCS.releasedir+= utils.cc + +SRCS.rename+= getmntopts.c +SRCS.rename+= mockfs.cc +SRCS.rename+= rename.cc +SRCS.rename+= utils.cc + +SRCS.rmdir+= getmntopts.c +SRCS.rmdir+= mockfs.cc +SRCS.rmdir+= rmdir.cc +SRCS.rmdir+= utils.cc + +SRCS.setattr+= getmntopts.c +SRCS.setattr+= mockfs.cc +SRCS.setattr+= setattr.cc +SRCS.setattr+= utils.cc + +SRCS.statfs+= getmntopts.c +SRCS.statfs+= mockfs.cc +SRCS.statfs+= statfs.cc +SRCS.statfs+= utils.cc + +SRCS.symlink+= getmntopts.c +SRCS.symlink+= mockfs.cc +SRCS.symlink+= symlink.cc +SRCS.symlink+= utils.cc + +SRCS.unlink+= getmntopts.c +SRCS.unlink+= mockfs.cc +SRCS.unlink+= unlink.cc +SRCS.unlink+= utils.cc + +SRCS.write+= getmntopts.c +SRCS.write+= mockfs.cc +SRCS.write+= write.cc +SRCS.write+= utils.cc + +SRCS.xattr+= getmntopts.c +SRCS.xattr+= mockfs.cc +SRCS.xattr+= xattr.cc +SRCS.xattr+= utils.cc + +# TODO: drastically increase timeout after test development is mostly complete +TEST_METADATA+= timeout=10 + +FUSEFS= ${.CURDIR:H:H:H:H}/sys/fs/fuse +MOUNT= ${.CURDIR:H:H:H:H}/sbin/mount +CFLAGS+= -I${.CURDIR:H:H:H} +CFLAGS+= -I${FUSEFS} +CFLAGS+= -I${MOUNT} +.PATH: ${MOUNT} +CXXSTD= c++14 + +# XXX Setting CXXFLAGS globally seems to be necessary to get mockfs.cc and +# utils.cc to build correctly. +CXXFLAGS+= ${GTESTS_CXXFLAGS} + +LIBADD+= pthread +LIBADD+= gmock gtest +LIBADD+= util + +WARNS?= 6 + +.include Property changes on: projects/fuse2/tests/sys/fs/fusefs/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/mockfs.hh =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.hh (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.hh (revision 345356) @@ -0,0 +1,239 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include + +#include + +#include "fuse_kernel.h" +} + +#include + +#define TIME_T_MAX (std::numeric_limits::max()) + +/* + * A pseudo-fuse errno used indicate that a fuse operation should have no + * response, at least not immediately + */ +#define FUSE_NORESPONSE 9999 + +#define SET_OUT_HEADER_LEN(out, variant) { \ + (out)->header.len = (sizeof((out)->header) + \ + sizeof((out)->body.variant)); \ +} + +/* + * Create an expectation on FUSE_LOOKUP and return it so the caller can set + * actions. + * + * This must be a macro instead of a method because EXPECT_CALL returns a type + * with a deleted constructor. + */ +#define EXPECT_LOOKUP(parent, path) \ + EXPECT_CALL(*m_mock, process( \ + ResultOf([=](auto in) { \ + return (in->header.opcode == FUSE_LOOKUP && \ + in->header.nodeid == (parent) && \ + strcmp(in->body.lookup, (path)) == 0); \ + }, Eq(true)), \ + _) \ + ) + +extern int verbosity; + +/* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */ +struct fuse_create_out { + struct fuse_entry_out entry; + struct fuse_open_out open; +}; + +union fuse_payloads_in { + fuse_access_in access; + /* value is from fuse_kern_chan.c in fusefs-libs */ + uint8_t bytes[0x21000 - sizeof(struct fuse_in_header)]; + fuse_flush_in flush; + fuse_fsync_in fsync; + fuse_fsync_in fsyncdir; + fuse_forget_in forget; + fuse_interrupt_in interrupt; + fuse_lk_in getlk; + fuse_getxattr_in getxattr; + fuse_init_in init; + fuse_link_in link; + fuse_listxattr_in listxattr; + char lookup[0]; + fuse_mkdir_in mkdir; + fuse_mknod_in mknod; + fuse_open_in open; + fuse_open_in opendir; + fuse_read_in read; + fuse_read_in readdir; + fuse_release_in release; + fuse_release_in releasedir; + fuse_rename_in rename; + char rmdir[0]; + fuse_setattr_in setattr; + fuse_setxattr_in setxattr; + fuse_lk_in setlk; + fuse_lk_in setlkw; + char unlink[0]; + fuse_write_in write; +}; + +struct mockfs_buf_in { + fuse_in_header header; + union fuse_payloads_in body; +}; + +union fuse_payloads_out { + fuse_attr_out attr; + fuse_create_out create; + /* The protocol places no limits on the size of bytes */ + uint8_t bytes[0x20000]; + fuse_entry_out entry; + fuse_lk_out getlk; + fuse_getxattr_out getxattr; + fuse_init_out init; + fuse_listxattr_out listxattr; + fuse_open_out open; + fuse_lk_out setlk; + fuse_lk_out setlkw; + fuse_statfs_out statfs; + /* + * The protocol places no limits on the length of the string. This is + * merely convenient for testing. + */ + char str[80]; + fuse_write_out write; +}; + +struct mockfs_buf_out { + fuse_out_header header; + union fuse_payloads_out body; + + /* Default constructor: zero everything */ + mockfs_buf_out() { + memset(this, 0, sizeof(*this)); + } +}; + +/* A function that can be invoked in place of MockFS::process */ +typedef std::function &out)> +ProcessMockerT; + +/* + * Helper function used for setting an error expectation for any fuse operation. + * The operation will return the supplied error + */ +ProcessMockerT ReturnErrno(int error); + +/* Helper function used for returning negative cache entries for LOOKUP */ +ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid); + +/* Helper function used for returning a single immediate response */ +ProcessMockerT ReturnImmediate( + std::function f); + +/* + * Fake FUSE filesystem + * + * "Mounts" a filesystem to a temporary directory and services requests + * according to the programmed expectations. + * + * Operates directly on the fusefs(4) kernel API, not the libfuse(3) user api. + */ +class MockFS { + /* + * thread id of the fuse daemon thread + * + * It must run in a separate thread so it doesn't deadlock with the + * client test code. + */ + pthread_t m_daemon_id; + + /* file descriptor of /dev/fuse control device */ + int m_fuse_fd; + + /* The max_readahead filesystem option */ + uint32_t m_maxreadahead; + + /* pid of the test process */ + pid_t m_pid; + + /* Initialize a session after mounting */ + void init(uint32_t flags); + + /* Is pid from a process that might be involved in the test? */ + bool pid_ok(pid_t pid); + + /* Default request handler */ + void process_default(const mockfs_buf_in*, + std::vector&); + + /* Entry point for the daemon thread */ + static void* service(void*); + + /* Read, but do not process, a single request from the kernel */ + void read_request(mockfs_buf_in*); + + public: + /* Maximum size of a FUSE_WRITE write */ + uint32_t m_max_write; + + /* Create a new mockfs and mount it to a tempdir */ + MockFS(int max_readahead, bool push_symlinks_in, + bool default_permissions, uint32_t flags); + virtual ~MockFS(); + + /* Kill the filesystem daemon without unmounting the filesystem */ + void kill_daemon(); + + /* Process FUSE requests endlessly */ + void loop(); + + /* + * Request handler + * + * This method is expected to provide the responses to each FUSE + * operation. For an immediate response, push one buffer into out. + * For a delayed response, push nothing. For an immediate response + * plus a delayed response to an earlier operation, push two bufs. + * Test cases must define each response using Googlemock expectations + */ + MOCK_METHOD2(process, void(const mockfs_buf_in*, + std::vector&)); + + /* Gracefully unmount */ + void unmount(); +}; Index: projects/fuse2/tests/sys/fs/fusefs/utils.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/utils.cc (revision 345356) @@ -0,0 +1,261 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +/* Check that fusefs(4) is accessible and the current user can mount(2) */ +void check_environment() +{ + const char *mod_name = "fusefs"; + const char *devnode = "/dev/fuse"; + const char *usermount_node = "vfs.usermount"; + int usermount_val = 0; + size_t usermount_size = sizeof(usermount_val); + if (modfind(mod_name) == -1) { + GTEST_SKIP() << "Module " << mod_name << + " could not be resolved"; + } + if (eaccess(devnode, R_OK | W_OK)) { + if (errno == ENOENT) { + GTEST_SKIP() << devnode << " does not exist"; + } else if (errno == EACCES) { + GTEST_SKIP() << devnode << + " is not accessible by the current user"; + } else { + GTEST_SKIP() << strerror(errno); + } + } + sysctlbyname(usermount_node, &usermount_val, &usermount_size, + NULL, 0); + if (geteuid() != 0 && !usermount_val) + GTEST_SKIP() << "current user is not allowed to mount"; +} + +class FuseEnv: public Environment { + virtual void SetUp() { + } +}; + +void FuseTest::SetUp() { + const char *node = "vfs.maxbcachebuf"; + int val = 0; + size_t size = sizeof(val); + + /* + * XXX check_environment should be called from FuseEnv::SetUp, but + * can't due to https://github.com/google/googletest/issues/2189 + */ + check_environment(); + if (IsSkipped()) + return; + + ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) + << strerror(errno); + m_maxbcachebuf = val; + + try { + m_mock = new MockFS(m_maxreadahead, m_push_symlinks_in, + m_default_permissions, m_init_flags); + } catch (std::system_error err) { + FAIL() << err.what(); + } +} + +void FuseTest::expect_getattr(uint64_t ino, uint64_t size) +{ + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + out->body.attr.attr.size = size; + out->body.attr.attr_valid = UINT64_MAX; + }))); +} + +void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode, + uint64_t size, int times) +{ + EXPECT_LOOKUP(1, relpath) + .Times(times) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = mode; + out->body.entry.nodeid = ino; + out->body.entry.attr.nlink = 1; + out->body.entry.attr_valid = UINT64_MAX; + out->body.entry.attr.size = size; + }))); +} + +void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).Times(times) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + out->body.open.fh = FH; + out->body.open.open_flags = flags; + }))); +} + +void FuseTest::expect_opendir(uint64_t ino) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_STATFS); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, statfs); + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPENDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + out->body.open.fh = FH; + }))); +} + +void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize, + uint64_t osize, const void *contents) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == FH && + in->body.read.offset == offset && + in->body.read.size == isize); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(struct fuse_out_header) + osize; + memmove(out->body.bytes, contents, osize); + }))).RetiresOnSaturation(); +} + +void FuseTest::expect_release(uint64_t ino, int times, uint64_t lock_owner, + int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_RELEASE && + in->header.nodeid == ino && + in->body.release.lock_owner == lock_owner && + in->body.release.fh == FH); + }, Eq(true)), + _) + ).Times(times) + .WillRepeatedly(Invoke(ReturnErrno(error))); +} +void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize, + uint64_t osize, uint32_t flags, const void *contents) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *buf = (const char*)in->body.bytes + + sizeof(struct fuse_write_in); + bool pid_ok; + + if (in->body.write.write_flags & FUSE_WRITE_CACHE) + pid_ok = true; + else + pid_ok = (pid_t)in->header.pid == getpid(); + + return (in->header.opcode == FUSE_WRITE && + in->header.nodeid == ino && + in->body.write.fh == FH && + in->body.write.offset == offset && + in->body.write.size == isize && + pid_ok && + in->body.write.write_flags == flags && + 0 == bcmp(buf, contents, isize)); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, write); + out->body.write.size = osize; + }))); +} + +static void usage(char* progname) { + fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname); + exit(2); +} + +int main(int argc, char **argv) { + int ch; + FuseEnv *fuse_env = new FuseEnv; + + InitGoogleTest(&argc, argv); + AddGlobalTestEnvironment(fuse_env); + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + verbosity++; + break; + default: + usage(argv[0]); + break; + } + } + + return (RUN_ALL_TESTS()); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/utils.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/write.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/write.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/write.cc (revision 345356) @@ -0,0 +1,659 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +#include +#include + +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Write: public FuseTest { + +public: + +void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); +} + +void expect_release(uint64_t ino, ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_RELEASE && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(r)); +} + +void require_sync_resize_0() { + const char *sync_resize_node = "vfs.fusefs.sync_resize"; + int val = 0; + size_t size = sizeof(val); + + ASSERT_EQ(0, sysctlbyname(sync_resize_node, &val, &size, NULL, 0)) + << strerror(errno); + if (val != 0) + GTEST_SKIP() << + "vfs.fusefs.sync_resize must be set to 0 for this test." + " That sysctl will probably be removed soon."; +} + +}; + +class AioWrite: public Write { +virtual void SetUp() { + const char *node = "vfs.aio.enable_unsafe"; + int val = 0; + size_t size = sizeof(val); + + FuseTest::SetUp(); + + ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) + << strerror(errno); + if (!val) + GTEST_SKIP() << + "vfs.aio.enable_unsafe must be set for this test"; +} +}; + +/* Tests for the write-through cache mode */ +class WriteThrough: public Write { + +virtual void SetUp() { + const char *cache_mode_node = "vfs.fusefs.data_cache_mode"; + int val = 0; + size_t size = sizeof(val); + + FuseTest::SetUp(); + if (IsSkipped()) + return; + + ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0)) + << strerror(errno); + if (val != 1) + GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 " + "(writethrough) for this test"; +} + +}; + +/* Tests for the writeback cache mode */ +class WriteBack: public Write { + +virtual void SetUp() { + const char *node = "vfs.fusefs.data_cache_mode"; + int val = 0; + size_t size = sizeof(val); + + FuseTest::SetUp(); + if (IsSkipped()) + return; + + ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) + << strerror(errno); + if (val != 2) + GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 " + "(writeback) for this test"; +} + +}; + +/* AIO writes need to set the header's pid field correctly */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ +TEST_F(AioWrite, DISABLED_aio_write) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + uint64_t offset = 4096; + int fd; + ssize_t bufsize = strlen(CONTENTS); + struct aiocb iocb, *piocb; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + iocb.aio_nbytes = bufsize; + iocb.aio_fildes = fd; + iocb.aio_buf = (void *)CONTENTS; + iocb.aio_offset = offset; + iocb.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno); + ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * When a file is opened with O_APPEND, we should forward that flag to + * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the + * offset internally. That way we'll work both with filesystems that + * understand O_APPEND (and ignore the offset) and filesystems that don't (and + * simply use the offset). + * + * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the + * Open.o_append test. + */ +TEST_F(Write, append) +{ + const ssize_t BUFSIZE = 9; + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char CONTENTS[BUFSIZE] = "abcdefgh"; + uint64_t ino = 42; + /* + * Set offset to a maxbcachebuf boundary so we don't need to RMW when + * using writeback caching + */ + uint64_t initial_offset = m_maxbcachebuf; + int fd; + + require_sync_resize_0(); + + expect_lookup(RELPATH, ino, initial_offset); + expect_open(ino, 0, 1); + expect_getattr(ino, initial_offset); + expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS); + + /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ + fd = open(FULLPATH, O_RDWR | O_APPEND); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +TEST_F(Write, append_direct_io) +{ + const ssize_t BUFSIZE = 9; + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char CONTENTS[BUFSIZE] = "abcdefgh"; + uint64_t ino = 42; + uint64_t initial_offset = 4096; + int fd; + + expect_lookup(RELPATH, ino, initial_offset); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, initial_offset); + expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS); + + fd = open(FULLPATH, O_WRONLY | O_APPEND); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* A direct write should evict any overlapping cached data */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235774 */ +TEST_F(Write, DISABLED_direct_io_evicts_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char CONTENTS0[] = "abcdefgh"; + const char CONTENTS1[] = "ijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS0) + 1; + char readbuf[bufsize]; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + // Prime cache + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + + // Write directly, evicting cache + ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); + + // Read again. Cache should be bypassed + expect_read(ino, 0, bufsize, bufsize, CONTENTS1); + ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + ASSERT_STREQ(readbuf, CONTENTS1); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * When the direct_io option is used, filesystems are allowed to write less + * data than requested + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */ +TEST_F(Write, DISABLED_direct_io_short_write) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefghijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + ssize_t halfbufsize = bufsize / 2; + const char *halfcontents = CONTENTS + halfbufsize; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS); + expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0, + halfcontents); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * An insidious edge case: the filesystem returns a short write, and the + * difference between what we requested and what it actually wrote crosses an + * iov element boundary + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236381 */ +TEST_F(Write, DISABLED_direct_io_short_write_iov) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS0 = "abcdefgh"; + const char *CONTENTS1 = "ijklmnop"; + const char *EXPECTED0 = "abcdefghijklmnop"; + const char *EXPECTED1 = "hijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t size0 = strlen(CONTENTS0) - 1; + ssize_t size1 = strlen(CONTENTS1) + 1; + ssize_t totalsize = size0 + size1; + struct iovec iov[2]; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, totalsize, size0, 0, EXPECTED0); + expect_write(ino, size0, size1, size1, 0, EXPECTED1); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + iov[0].iov_base = (void*)CONTENTS0; + iov[0].iov_len = strlen(CONTENTS0); + iov[1].iov_base = (void*)CONTENTS1; + iov[1].iov_len = strlen(CONTENTS1); + ASSERT_EQ(totalsize, writev(fd, iov, 2)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the kernel cannot be sure which uid, gid, or pid was responsible for a + * write, then it must set the FUSE_WRITE_CACHE bit + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ +// TODO: check vfs.fusefs.mmap_enable +TEST_F(Write, DISABLED_mmap) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + void *p; + uint64_t offset = 10; + size_t len; + void *zeros, *expected; + + len = getpagesize(); + + zeros = calloc(1, len); + ASSERT_NE(NULL, zeros); + expected = calloc(1, len); + ASSERT_NE(NULL, expected); + memmove((uint8_t*)expected + offset, CONTENTS, bufsize); + + expect_lookup(RELPATH, ino, len); + expect_open(ino, 0, 1); + expect_getattr(ino, len); + expect_read(ino, 0, len, len, zeros); + /* + * Writes from the pager may or may not be associated with the correct + * pid, so they must set FUSE_WRITE_CACHE + */ + expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, expected); + expect_release(ino, ReturnErrno(0)); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ASSERT_NE(MAP_FAILED, p) << strerror(errno); + + memmove((uint8_t*)p + offset, CONTENTS, bufsize); + + ASSERT_EQ(0, munmap(p, len)) << strerror(errno); + close(fd); // Write mmap'd data on close + + free(expected); + free(zeros); +} + +TEST_F(Write, pwrite) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + uint64_t offset = 4096; + int fd; + ssize_t bufsize = strlen(CONTENTS); + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) + << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +TEST_F(Write, write) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* fuse(4) should not issue writes of greater size than the daemon requests */ +TEST_F(Write, write_large) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + int *contents; + uint64_t ino = 42; + int fd; + ssize_t halfbufsize, bufsize; + + halfbufsize = m_mock->m_max_write; + bufsize = halfbufsize * 2; + contents = (int*)malloc(bufsize); + ASSERT_NE(NULL, contents); + for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { + contents[i] = i; + } + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents); + expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0, + &contents[halfbufsize / sizeof(int)]); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ + + free(contents); +} + +TEST_F(Write, write_nothing) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = ""; + uint64_t ino = 42; + int fd; + ssize_t bufsize = 0; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* In writeback mode, dirty data should be written on close */ +TEST_F(WriteBack, close) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETATTR); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, attr); + out->body.attr.attr.ino = ino; // Must match nodeid + }))); + expect_release(ino, ReturnErrno(0)); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + close(fd); +} + +/* + * Without direct_io, writes should be committed to cache + */ +TEST_F(WriteBack, writeback) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char readbuf[bufsize]; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* + * A subsequent read should be serviced by cache, without querying the + * filesystem daemon + */ + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * With O_DIRECT, writes should be not committed to cache. Admittedly this is + * an odd test, because it would be unusual to use O_DIRECT for writes but not + * reads. + */ +TEST_F(WriteBack, o_direct) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char readbuf[bufsize]; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDWR | O_DIRECT); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* A subsequent read must query the daemon because cache is empty */ + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Without direct_io, writes should be committed to cache + */ +/* + * Disabled because we don't yet implement write-through caching. No bugzilla + * entry, because that's a feature request, not a bug. + */ +TEST_F(WriteThrough, DISABLED_writethrough) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char readbuf[bufsize]; + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* + * A subsequent read should be serviced by cache, without querying the + * filesystem daemon + */ + ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* With writethrough caching, writes update the cached file size */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ +TEST_F(WriteThrough, DISABLED_update_file_size) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + struct stat sb; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + + expect_lookup(RELPATH, ino, 0); + expect_open(ino, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).Times(2) + .WillRepeatedly(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 | 0644; + out->body.attr.attr.size = 0; + out->body.attr.attr_valid = UINT64_MAX; + }))); + expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS); + + fd = open(FULLPATH, O_RDWR); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + /* Get cached attributes */ + ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); + ASSERT_EQ(bufsize, sb.st_size); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/write.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc (revision 345356) @@ -0,0 +1,137 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Tests for the "default_permissions" mount option. They must be in their own + * file so they can be run as an unprivileged user + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class DefaultPermissions: public FuseTest { + +virtual void SetUp() { + FuseTest::SetUp(); + + if (geteuid() == 0) { + GTEST_SKIP() << "This test requires an unprivileged user"; + } + m_default_permissions = true; +} + +public: +void expect_lookup(const char *relpath, uint64_t ino, mode_t mode) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | mode, 0, 1); +} + +}; + +class Access: public DefaultPermissions {}; +class Open: public DefaultPermissions {}; + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ +TEST_F(Access, DISABLED_eaccess) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + mode_t access_mode = X_OK; + + expect_lookup(RELPATH, ino, S_IFREG | 0644); + /* + * Once default_permissions is properly implemented, there might be + * another FUSE_GETATTR or something in here. But there should not be + * a FUSE_ACCESS + */ + + ASSERT_NE(0, access(FULLPATH, access_mode)); + ASSERT_EQ(EACCES, errno); +} + +TEST_F(Access, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + mode_t access_mode = R_OK; + + expect_lookup(RELPATH, ino, S_IFREG | 0644); + /* + * Once default_permissions is properly implemented, there might be + * another FUSE_GETATTR or something in here. But there should not be + * a FUSE_ACCESS + */ + + ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); +} + +TEST_F(Open, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino, S_IFREG | 0644); + expect_open(ino, 0, 1); + /* Until the attr cache is working, we may send an additional GETATTR */ + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_RDONLY); + EXPECT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ +TEST_F(Open, DISABLED_eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, S_IFREG | 0644); + /* + * Once default_permissions is properly implemented, there might be + * another FUSE_GETATTR or something in here. But there should not be + * a FUSE_ACCESS + */ + + EXPECT_NE(0, open(FULLPATH, O_RDWR)); + EXPECT_EQ(EPERM, errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/default_permissions.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/mknod.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mknod.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/mknod.cc (revision 345356) @@ -0,0 +1,138 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Mknod: public FuseTest { + +public: + +virtual void SetUp() { + FuseTest::SetUp(); + + if (geteuid() != 0) { + GTEST_SKIP() << "Only root may use most mknod(2) variations"; + } +} + +/* Test an OK creation of a file with the given mode and device number */ +void test_ok(mode_t mode, dev_t dev) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_mknod_in); + return (in->header.opcode == FUSE_MKNOD && + in->body.mknod.mode == mode && + in->body.mknod.rdev == dev && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + out->body.create.entry.attr.rdev = dev; + }))); + EXPECT_EQ(0, mknod(FULLPATH, mode, dev)) << strerror(errno); +} + +}; + +/* + * mknod(2) should be able to create block devices on a FUSE filesystem. Even + * though FreeBSD doesn't use block devices, this is useful when copying media + * from or preparing media for other operating systems. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +TEST_F(Mknod, DISABLED_blk) +{ + test_ok(S_IFBLK | 0755, 0xfe00); /* /dev/vda's device number on Linux */ +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +TEST_F(Mknod, DISABLED_chr) +{ + test_ok(S_IFCHR | 0755, 0x64); /* /dev/fuse's device number */ +} + +/* + * The daemon is responsible for checking file permissions (unless the + * default_permissions mount option was used) + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +TEST_F(Mknod, DISABLED_eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = S_IFIFO | 0755; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_mknod_in); + return (in->header.opcode == FUSE_MKNOD && + in->body.mknod.mode == mode && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EPERM))); + EXPECT_NE(0, mknod(FULLPATH, mode, 0)); + EXPECT_EQ(EPERM, errno); +} + + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +TEST_F(Mknod, DISABLED_fifo) +{ + test_ok(S_IFIFO | 0755, 0); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +TEST_F(Mknod, DISABLED_whiteout) +{ + test_ok(S_IFWHT | 0755, 0); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/mknod.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/read.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/read.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/read.cc (revision 345356) @@ -0,0 +1,739 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +#include +#include + +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Read: public FuseTest { + +public: +void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); +} +}; + +class AioRead: public Read { +public: +virtual void SetUp() { + const char *node = "vfs.aio.enable_unsafe"; + int val = 0; + size_t size = sizeof(val); + + FuseTest::SetUp(); + + ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) + << strerror(errno); + if (!val) + GTEST_SKIP() << + "vfs.aio.enable_unsafe must be set for this test"; +} +}; + +class AsyncRead: public AioRead { + virtual void SetUp() { + m_init_flags = FUSE_ASYNC_READ; + AioRead::SetUp(); + } +}; + +class ReadAhead: public Read, public WithParamInterface { + virtual void SetUp() { + m_maxreadahead = GetParam(); + Read::SetUp(); + } +}; + +/* AIO reads need to set the header's pid field correctly */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ +TEST_F(AioRead, aio_read) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + struct aiocb iocb, *piocb; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + iocb.aio_nbytes = bufsize; + iocb.aio_fildes = fd; + iocb.aio_buf = buf; + iocb.aio_offset = 0; + iocb.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno); + ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there + * is at most one outstanding read operation per file handle + */ +TEST_F(AioRead, async_read_disabled) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = 50; + char buf0[bufsize], buf1[bufsize]; + off_t off0 = 0; + off_t off1 = 4096; + struct aiocb iocb0, iocb1; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == FH && + in->body.read.offset == (uint64_t)off0 && + in->body.read.size == bufsize); + }, Eq(true)), + _) + ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { + /* Filesystem is slow to respond */ + })); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == FH && + in->body.read.offset == (uint64_t)off1 && + in->body.read.size == bufsize); + }, Eq(true)), + _) + ).Times(0); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + /* + * Submit two AIO read requests, and respond to neither. If the + * filesystem ever gets the second read request, then we failed to + * limit outstanding reads. + */ + iocb0.aio_nbytes = bufsize; + iocb0.aio_fildes = fd; + iocb0.aio_buf = buf0; + iocb0.aio_offset = off0; + iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); + + iocb1.aio_nbytes = bufsize; + iocb1.aio_fildes = fd; + iocb1.aio_buf = buf1; + iocb1.aio_offset = off1; + iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); + + /* + * Sleep for awhile to make sure the kernel has had a chance to issue + * the second read, even though the first has not yet returned + */ + usleep(250'000); + + /* Deliberately leak iocbs */ + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple + * simultaneous read requests on the same file handle. + */ +/* + * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla + * entry, because that's a feature request, not a bug. + */ +TEST_F(AsyncRead, DISABLED_async_read) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = 50; + char buf0[bufsize], buf1[bufsize]; + off_t off0 = 0; + off_t off1 = 4096; + struct aiocb iocb0, iocb1; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == FH && + in->body.read.offset == (uint64_t)off0 && + in->body.read.size == bufsize); + }, Eq(true)), + _) + ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { + /* Filesystem is slow to respond */ + })); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == FH && + in->body.read.offset == (uint64_t)off1 && + in->body.read.size == bufsize); + }, Eq(true)), + _) + ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { + /* Filesystem is slow to respond */ + })); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + /* + * Submit two AIO read requests, but respond to neither. Ensure that + * we received both. + */ + iocb0.aio_nbytes = bufsize; + iocb0.aio_fildes = fd; + iocb0.aio_buf = buf0; + iocb0.aio_offset = off0; + iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); + + iocb1.aio_nbytes = bufsize; + iocb1.aio_fildes = fd; + iocb1.aio_buf = buf1; + iocb1.aio_offset = off1; + iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; + ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); + + /* + * Sleep for awhile to make sure the kernel has had a chance to issue + * both reads. + */ + usleep(250'000); + + /* Deliberately leak iocbs */ + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* 0-length reads shouldn't cause any confusion */ +TEST_F(Read, direct_io_read_nothing) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + uint64_t offset = 100; + char buf[80]; + + expect_lookup(RELPATH, ino, offset + 1000); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, offset + 1000); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * With direct_io, reads should not fill the cache. They should go straight to + * the daemon + */ +TEST_F(Read, direct_io_pread) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + uint64_t offset = 100; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + expect_lookup(RELPATH, ino, offset + bufsize); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, offset + bufsize); + expect_read(ino, offset, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * With direct_io, filesystems are allowed to return less data than is + * requested. fuse(4) should return a short read to userland. + */ +TEST_F(Read, direct_io_short_read) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefghijklmnop"; + uint64_t ino = 42; + int fd; + uint64_t offset = 100; + ssize_t bufsize = strlen(CONTENTS); + ssize_t halfbufsize = bufsize / 2; + char buf[bufsize]; + + expect_lookup(RELPATH, ino, offset + bufsize); + expect_open(ino, FOPEN_DIRECT_IO, 1); + expect_getattr(ino, offset + bufsize); + expect_read(ino, offset, bufsize, halfbufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset)) + << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +TEST_F(Read, eio) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EIO))); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(EIO, errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * With the keep_cache option, the kernel may keep its read cache across + * multiple open(2)s. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236560 */ +TEST_F(Read, DISABLED_keep_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd0, fd1; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); + expect_open(ino, FOPEN_KEEP_CACHE, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd0 = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd0) << strerror(errno); + ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); + + fd1 = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd1) << strerror(errno); + + /* + * This read should be serviced by cache, even though it's on the other + * file descriptor + */ + ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno); + + /* Deliberately leak fd0 and fd1. */ +} + +/* + * Without the keep_cache option, the kernel should drop its read caches on + * every open + */ +TEST_F(Read, keep_cache_disabled) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd0, fd1; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); + expect_open(ino, FOPEN_KEEP_CACHE, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd0 = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd0) << strerror(errno); + ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); + + fd1 = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd1) << strerror(errno); + + /* + * This read should not be serviced by cache, even though it's on the + * original file descriptor + */ + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); + + /* Deliberately leak fd0 and fd1. */ +} + +TEST_F(Read, mmap) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t len; + ssize_t bufsize = strlen(CONTENTS); + void *p; + //char buf[bufsize]; + + len = getpagesize(); + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + /* mmap may legitimately try to read more data than is available */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == Read::FH && + in->body.read.offset == 0 && + in->body.read.size >= bufsize); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(struct fuse_out_header) + bufsize; + memmove(out->body.bytes, CONTENTS, bufsize); + }))); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + ASSERT_NE(MAP_FAILED, p) << strerror(errno); + + ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); + + ASSERT_EQ(0, munmap(p, len)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass + * cache and to straight to the daemon + */ +TEST_F(Read, o_direct) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + // Fill the cache + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + + // Reads with o_direct should bypass the cache + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); + ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +TEST_F(Read, pread) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + /* + * Set offset to a maxbcachebuf boundary so we'll be sure what offset + * to read from. Without this, the read might start at a lower offset. + */ + uint64_t offset = m_maxbcachebuf; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + expect_lookup(RELPATH, ino, offset + bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, offset + bufsize); + expect_read(ino, offset, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +TEST_F(Read, read) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + expect_read(ino, 0, bufsize, bufsize, CONTENTS); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* If the filesystem allows it, the kernel should try to readahead */ +TEST_F(Read, default_readahead) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS0 = "abcdefghijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = 8; + /* hard-coded in fuse_internal.c */ + size_t default_maxreadahead = 65536; + ssize_t filesize = default_maxreadahead * 2; + char *contents; + char buf[bufsize]; + const char *contents1 = CONTENTS0 + bufsize; + + contents = (char*)calloc(1, filesize); + ASSERT_NE(NULL, contents); + memmove(contents, CONTENTS0, strlen(CONTENTS0)); + + expect_lookup(RELPATH, ino, filesize); + expect_open(ino, 0, 1); + expect_getattr(ino, filesize); + expect_read(ino, 0, default_maxreadahead, default_maxreadahead, + contents); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); + + /* A subsequent read should be serviced by cache */ + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, contents1, bufsize)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Reading with sendfile should work (though it obviously won't be 0-copy) */ +TEST_F(Read, sendfile) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + char buf[bufsize]; + int sp[2]; + off_t sbytes; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + /* Like mmap, sendfile may request more data than is available */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ && + in->header.nodeid == ino && + in->body.read.fh == Read::FH && + in->body.read.offset == 0 && + in->body.read.size >= bufsize); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(struct fuse_out_header) + bufsize; + memmove(out->body.bytes, CONTENTS, bufsize); + }))); + + ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) + << strerror(errno); + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)) + << strerror(errno); + ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); + + close(sp[1]); + close(sp[0]); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* sendfile should fail gracefully if fuse declines the read */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */ +TEST_F(Read, DISABLED_sendfile_eio) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + int sp[2]; + off_t sbytes; + + expect_lookup(RELPATH, ino, bufsize); + expect_open(ino, 0, 1); + expect_getattr(ino, bufsize); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READ); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EIO))); + + ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) + << strerror(errno); + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)); + + close(sp[1]); + close(sp[0]); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* fuse(4) should honor the filesystem's requested m_readahead parameter */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */ +TEST_P(ReadAhead, DISABLED_readahead) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS0 = "abcdefghijklmnop"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = 8; + ssize_t filesize = m_maxbcachebuf * 2; + char *contents; + char buf[bufsize]; + + ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf) + << "Test assumes that max_readahead < maxbcachebuf"; + + contents = (char*)calloc(1, filesize); + ASSERT_NE(NULL, contents); + memmove(contents, CONTENTS0, strlen(CONTENTS0)); + + expect_lookup(RELPATH, ino, filesize); + expect_open(ino, 0, 1); + expect_getattr(ino, filesize); + /* fuse(4) should only read ahead the allowed amount */ + expect_read(ino, 0, GetParam(), GetParam(), contents); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); + ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u)); Property changes on: projects/fuse2/tests/sys/fs/fusefs/read.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mockfs.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/mockfs.cc (revision 345356) @@ -0,0 +1,447 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mntopts.h" // for build_iovec +} + +#include + +#include "mockfs.hh" + +using namespace testing; + +int verbosity = 0; +static sig_atomic_t quit = 0; + +const char* opcode2opname(uint32_t opcode) +{ + const int NUM_OPS = 39; + const char* table[NUM_OPS] = { + "Unknown (opcode 0)", + "LOOKUP", + "FORGET", + "GETATTR", + "SETATTR", + "READLINK", + "SYMLINK", + "Unknown (opcode 7)", + "MKNOD", + "MKDIR", + "UNLINK", + "RMDIR", + "RENAME", + "LINK", + "OPEN", + "READ", + "WRITE", + "STATFS", + "RELEASE", + "Unknown (opcode 19)", + "FSYNC", + "SETXATTR", + "GETXATTR", + "LISTXATTR", + "REMOVEXATTR", + "FLUSH", + "INIT", + "OPENDIR", + "READDIR", + "RELEASEDIR", + "FSYNCDIR", + "GETLK", + "SETLK", + "SETLKW", + "ACCESS", + "CREATE", + "INTERRUPT", + "BMAP", + "DESTROY" + }; + if (opcode >= NUM_OPS) + return ("Unknown (opcode > max)"); + else + return (table[opcode]); +} + +ProcessMockerT +ReturnErrno(int error) +{ + return([=](auto in, auto &out) { + auto out0 = new mockfs_buf_out; + out0->header.unique = in->header.unique; + out0->header.error = -error; + out0->header.len = sizeof(out0->header); + out.push_back(out0); + }); +} + +/* Helper function used for returning negative cache entries for LOOKUP */ +ProcessMockerT +ReturnNegativeCache(const struct timespec *entry_valid) +{ + return([=](auto in, auto &out) { + /* nodeid means ENOENT and cache it */ + auto out0 = new mockfs_buf_out; + out0->body.entry.nodeid = 0; + out0->header.unique = in->header.unique; + out0->header.error = 0; + out0->body.entry.entry_valid = entry_valid->tv_sec; + out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; + SET_OUT_HEADER_LEN(out0, entry); + out.push_back(out0); + }); +} + +ProcessMockerT +ReturnImmediate(std::function f) +{ + return([=](auto in, auto &out) { + auto out0 = new mockfs_buf_out; + out0->header.unique = in->header.unique; + f(in, out0); + out.push_back(out0); + }); +} + +void sigint_handler(int __unused sig) { + quit = 1; +} + +void debug_fuseop(const mockfs_buf_in *in) +{ + printf("%-11s ino=%2lu", opcode2opname(in->header.opcode), + in->header.nodeid); + if (verbosity > 1) { + printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u", + in->header.uid, in->header.gid, in->header.pid, + in->header.unique, in->header.len); + } + switch (in->header.opcode) { + case FUSE_FLUSH: + printf(" lock_owner=%lu", in->body.flush.lock_owner); + break; + case FUSE_FORGET: + printf(" nlookup=%lu", in->body.forget.nlookup); + break; + case FUSE_FSYNC: + printf(" flags=%#x", in->body.fsync.fsync_flags); + break; + case FUSE_FSYNCDIR: + printf(" flags=%#x", in->body.fsyncdir.fsync_flags); + break; + case FUSE_LOOKUP: + printf(" %s", in->body.lookup); + break; + case FUSE_OPEN: + printf(" flags=%#x mode=%#o", + in->body.open.flags, in->body.open.mode); + break; + case FUSE_OPENDIR: + printf(" flags=%#x mode=%#o", + in->body.opendir.flags, in->body.opendir.mode); + break; + case FUSE_READ: + printf(" offset=%lu size=%u", in->body.read.offset, + in->body.read.size); + break; + case FUSE_READDIR: + printf(" offset=%lu size=%u", in->body.readdir.offset, + in->body.readdir.size); + break; + case FUSE_RELEASE: + printf(" flags=%#x lock_owner=%lu", + in->body.release.flags, + in->body.release.lock_owner); + break; + case FUSE_SETATTR: + if (verbosity <= 1) { + printf(" valid=%#x", in->body.setattr.valid); + break; + } + if (in->body.setattr.valid & FATTR_MODE) + printf(" mode=%#o", in->body.setattr.mode); + if (in->body.setattr.valid & FATTR_UID) + printf(" uid=%u", in->body.setattr.uid); + if (in->body.setattr.valid & FATTR_GID) + printf(" gid=%u", in->body.setattr.gid); + if (in->body.setattr.valid & FATTR_SIZE) + printf(" size=%zu", in->body.setattr.size); + if (in->body.setattr.valid & FATTR_ATIME) + printf(" atime=%zu.%u", + in->body.setattr.atime, + in->body.setattr.atimensec); + if (in->body.setattr.valid & FATTR_MTIME) + printf(" mtime=%zu.%u", + in->body.setattr.mtime, + in->body.setattr.mtimensec); + if (in->body.setattr.valid & FATTR_FH) + printf(" fh=%zu", in->body.setattr.fh); + break; + case FUSE_SETXATTR: + /* + * In theory neither the xattr name and value need be + * ASCII, but in this test suite they always are. + */ + { + const char *attr = (const char*)in->body.bytes + + sizeof(fuse_setxattr_in); + const char *v = attr + strlen(attr) + 1; + printf(" %s=%s", attr, v); + } + break; + case FUSE_WRITE: + printf(" offset=%lu size=%u flags=%u", + in->body.write.offset, in->body.write.size, + in->body.write.write_flags); + break; + default: + break; + } + printf("\n"); +} + +MockFS::MockFS(int max_readahead, bool push_symlinks_in, + bool default_permissions, uint32_t flags) +{ + struct iovec *iov = NULL; + int iovlen = 0; + char fdstr[15]; + + m_daemon_id = NULL; + m_maxreadahead = max_readahead; + quit = 0; + + /* + * Kyua sets pwd to a testcase-unique tempdir; no need to use + * mkdtemp + */ + /* + * googletest doesn't allow ASSERT_ in constructors, so we must throw + * instead. + */ + if (mkdir("mountpoint" , 0644) && errno != EEXIST) + throw(std::system_error(errno, std::system_category(), + "Couldn't make mountpoint directory")); + + m_fuse_fd = open("/dev/fuse", O_RDWR); + if (m_fuse_fd < 0) + throw(std::system_error(errno, std::system_category(), + "Couldn't open /dev/fuse")); + sprintf(fdstr, "%d", m_fuse_fd); + + m_pid = getpid(); + + build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); + build_iovec(&iov, &iovlen, "fspath", + __DECONST(void *, "mountpoint"), -1); + build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); + build_iovec(&iov, &iovlen, "fd", fdstr, -1); + if (push_symlinks_in) { + const bool trueval = true; + build_iovec(&iov, &iovlen, "push_symlinks_in", + __DECONST(void*, &trueval), sizeof(bool)); + } + if (default_permissions) { + const bool trueval = true; + build_iovec(&iov, &iovlen, "default_permissions", + __DECONST(void*, &trueval), sizeof(bool)); + } + if (nmount(iov, iovlen, 0)) + throw(std::system_error(errno, std::system_category(), + "Couldn't mount filesystem")); + + // Setup default handler + ON_CALL(*this, process(_, _)) + .WillByDefault(Invoke(this, &MockFS::process_default)); + + init(flags); + signal(SIGUSR1, sigint_handler); + if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) + throw(std::system_error(errno, std::system_category(), + "Couldn't Couldn't start fuse thread")); +} + +MockFS::~MockFS() { + kill_daemon(); + ::unmount("mountpoint", MNT_FORCE); + if (m_daemon_id != NULL) { + pthread_join(m_daemon_id, NULL); + m_daemon_id = NULL; + } + rmdir("mountpoint"); +} + +void MockFS::init(uint32_t flags) { + mockfs_buf_in *in; + mockfs_buf_out *out; + + in = (mockfs_buf_in*) malloc(sizeof(*in)); + ASSERT_TRUE(in != NULL); + out = (mockfs_buf_out*) malloc(sizeof(*out)); + ASSERT_TRUE(out != NULL); + + read_request(in); + ASSERT_EQ(FUSE_INIT, in->header.opcode); + + memset(out, 0, sizeof(*out)); + out->header.unique = in->header.unique; + out->header.error = 0; + out->body.init.major = FUSE_KERNEL_VERSION; + out->body.init.minor = FUSE_KERNEL_MINOR_VERSION; + out->body.init.flags = in->body.init.flags & flags; + + /* + * The default max_write is set to this formula in libfuse, though + * individual filesystems can lower it. The "- 4096" was added in + * commit 154ffe2, with the commit message "fix". + */ + uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096; + /* For testing purposes, it should be distinct from MAXPHYS */ + m_max_write = MIN(default_max_write, MAXPHYS / 2); + out->body.init.max_write = m_max_write; + + out->body.init.max_readahead = m_maxreadahead; + SET_OUT_HEADER_LEN(out, init); + write(m_fuse_fd, out, out->header.len); + + free(in); +} + +void MockFS::kill_daemon() { + if (m_daemon_id != NULL) { + pthread_kill(m_daemon_id, SIGUSR1); + // Closing the /dev/fuse file descriptor first allows unmount + // to succeed even if the daemon doesn't correctly respond to + // commands during the unmount sequence. + close(m_fuse_fd); + } +} + +void MockFS::loop() { + mockfs_buf_in *in; + std::vector out; + + in = (mockfs_buf_in*) malloc(sizeof(*in)); + ASSERT_TRUE(in != NULL); + while (!quit) { + bzero(in, sizeof(*in)); + read_request(in); + if (quit) + break; + if (verbosity > 0) + debug_fuseop(in); + if (pid_ok((pid_t)in->header.pid)) { + process(in, out); + } else { + /* + * Reject any requests from unknown processes. Because + * we actually do mount a filesystem, plenty of + * unrelated system daemons may try to access it. + */ + process_default(in, out); + } + for (auto &it: out) { + ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 || + errno == EAGAIN) + << strerror(errno); + delete it; + } + out.clear(); + } + free(in); +} + +bool MockFS::pid_ok(pid_t pid) { + if (pid == m_pid) { + return (true); + } else { + struct kinfo_proc *ki; + bool ok = false; + + ki = kinfo_getproc(pid); + if (ki == NULL) + return (false); + /* + * Allow access by the aio daemon processes so that our tests + * can use aio functions + */ + if (0 == strncmp("aiod", ki->ki_comm, 4)) + ok = true; + free(ki); + return (ok); + } +} + +void MockFS::process_default(const mockfs_buf_in *in, + std::vector &out) +{ + auto out0 = new mockfs_buf_out; + out0->header.unique = in->header.unique; + out0->header.error = -EOPNOTSUPP; + out0->header.len = sizeof(out0->header); + out.push_back(out0); +} + +void MockFS::read_request(mockfs_buf_in *in) { + ssize_t res; + + res = read(m_fuse_fd, in, sizeof(*in)); + if (res < 0 && !quit) + perror("read"); + ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || quit); +} + +void* MockFS::service(void *pthr_data) { + MockFS *mock_fs = (MockFS*)pthr_data; + + mock_fs->loop(); + + return (NULL); +} + +void MockFS::unmount() { + ::unmount("mountpoint", 0); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/mockfs.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/access.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/access.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/access.cc (revision 345356) @@ -0,0 +1,111 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Access: public FuseTest { +public: +void expect_access(uint64_t ino, mode_t access_mode, int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_ACCESS && + in->header.nodeid == ino && + in->body.access.mask == access_mode); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} +}; + +/* The error case of FUSE_ACCESS. */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ +TEST_F(Access, DISABLED_eaccess) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + mode_t access_mode = X_OK; + + expect_lookup(RELPATH, ino); + expect_access(ino, access_mode, EACCES); + + ASSERT_NE(0, access(FULLPATH, access_mode)); + ASSERT_EQ(EACCES, errno); +} + +/* + * If the filesystem returns ENOSYS, then it is treated as a permanent success, + * and subsequent VOP_ACCESS calls will succeed automatically without querying + * the daemon. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ +TEST_F(Access, DISABLED_enosys) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + mode_t access_mode = R_OK; + + expect_lookup(RELPATH, ino); + expect_access(ino, access_mode, ENOSYS); + + ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); + ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); +} + +/* The successful case of FUSE_ACCESS. */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236291 */ +TEST_F(Access, DISABLED_ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + mode_t access_mode = R_OK; + + expect_lookup(RELPATH, ino); + expect_access(ino, access_mode, 0); + + ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/access.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/readlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/readlink.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/readlink.cc (revision 345356) @@ -0,0 +1,122 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include + +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Readlink: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 0, 1); +} +void expect_readlink(uint64_t ino, ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READLINK && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(r)); +} + +}; + +class PushSymlinksIn: public Readlink { + virtual void SetUp() { + m_push_symlinks_in = true; + Readlink::SetUp(); + } +}; + +TEST_F(Readlink, eloop) +{ + const char FULLPATH[] = "mountpoint/src"; + const char RELPATH[] = "src"; + const uint64_t ino = 42; + char buf[80]; + + expect_lookup(RELPATH, ino); + expect_readlink(ino, ReturnErrno(ELOOP)); + + EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf))); + EXPECT_EQ(ELOOP, errno); +} + +TEST_F(Readlink, ok) +{ + const char FULLPATH[] = "mountpoint/src"; + const char RELPATH[] = "src"; + const char dst[] = "dst"; + const uint64_t ino = 42; + char buf[80]; + + expect_lookup(RELPATH, ino); + expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) { + strlcpy(out->body.str, dst, sizeof(out->body.str)); + out->header.len = sizeof(out->header) + strlen(dst) + 1; + })); + + EXPECT_EQ((ssize_t)strlen(dst) + 1, + readlink(FULLPATH, buf, sizeof(buf))); + EXPECT_STREQ(dst, buf); +} + +TEST_F(PushSymlinksIn, readlink) +{ + const char FULLPATH[] = "mountpoint/src"; + const char RELPATH[] = "src"; + const char dst[] = "/dst"; + const uint64_t ino = 42; + char buf[MAXPATHLEN], wd[MAXPATHLEN], want[MAXPATHLEN]; + int len; + + expect_lookup(RELPATH, ino); + expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto out) { + strlcpy(out->body.str, dst, sizeof(out->body.str)); + out->header.len = sizeof(out->header) + strlen(dst) + 1; + })); + + ASSERT_NE(NULL, getcwd(wd, sizeof(wd))) << strerror(errno); + len = snprintf(want, sizeof(want), "%s/mountpoint%s", wd, dst); + ASSERT_LE(0, len) << strerror(errno); + + EXPECT_EQ((ssize_t)len + 1, readlink(FULLPATH, buf, sizeof(buf))); + EXPECT_STREQ(want, buf); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/readlink.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/utils.hh =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/utils.hh (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/utils.hh (revision 345356) @@ -0,0 +1,120 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * TODO: remove FUSE_WRITE_CACHE definition when upgrading to protocol 7.9. + * This bit was actually part of kernel protocol version 7.2, but never + * documented until 7.9 + */ +#ifndef FUSE_WRITE_CACHE +#define FUSE_WRITE_CACHE 1 +#endif + +class FuseTest : public ::testing::Test { + protected: + uint32_t m_maxreadahead; + uint32_t m_init_flags; + bool m_default_permissions; + bool m_push_symlinks_in; + MockFS *m_mock = NULL; + const static uint64_t FH = 0xdeadbeef1a7ebabe; + + public: + int m_maxbcachebuf; + + FuseTest(): + /* + * libfuse's default max_readahead is UINT_MAX, though it can + * be lowered + */ + m_maxreadahead(UINT_MAX), + m_init_flags(0), + m_default_permissions(false), + m_push_symlinks_in(false) + {} + + virtual void SetUp(); + + virtual void TearDown() { + if (m_mock) + delete m_mock; + } + + /* + * Create an expectation that FUSE_GETATTR will be called for the given + * inode any number of times. It will respond with a few basic + * attributes, like the given size and the mode S_IFREG | 0644 + */ + void expect_getattr(uint64_t ino, uint64_t size); + + /* + * Create an expectation that FUSE_LOOKUP will be called for the given + * path exactly times times. It will respond with inode ino, mode + * mode, filesize size, and cache validity forever. + */ + void expect_lookup(const char *relpath, uint64_t ino, mode_t mode, + uint64_t size, int times); + + /* + * Create an expectation that FUSE_GETATTR will be called for the given + * inode exactly times times. It will return with open_flags flags and + * file handle FH. + */ + void expect_open(uint64_t ino, uint32_t flags, int times); + + /* + * Create an expectation that FUSE_OPENDIR will be called exactly once + * for inode ino. + */ + void expect_opendir(uint64_t ino); + + /* + * Create an expectation that FUSE_READ will be called exactly once for + * the given inode, at offset offset and with size isize. It will + * return the first osize bytes from contents + */ + void expect_read(uint64_t ino, uint64_t offset, uint64_t isize, + uint64_t osize, const void *contents); + + /* + * Create an expectation that FUSE_RELEASE will be called times times + * for the given inode, returning error error + */ + void expect_release(uint64_t ino, int times, uint64_t lock_owner, + int error); + + /* + * Create an expectation that FUSE_WRITE will be called exactly once + * for the given inode, at offset offset, with write_flags flags, + * size isize and buffer contents. It will return osize + */ + void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, + uint64_t osize, uint32_t flags, const void *contents); +}; Index: projects/fuse2/tests/sys/fs/fusefs/create.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/create.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/create.cc (revision 345356) @@ -0,0 +1,364 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Create: public FuseTest {}; + +/* + * If FUSE_CREATE sets the attr_valid, then subsequent GETATTRs should use the + * attribute cache + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ +TEST_F(Create, DISABLED_attr_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + int fd; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = S_IFREG | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).Times(0); + + fd = open(FULLPATH, O_CREAT | O_EXCL, mode); + EXPECT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * The fuse daemon fails the request with EEXIST. This usually indicates a + * race condition: some other FUSE client created the file in between when the + * kernel checked for it with lookup and tried to create it with create + */ +TEST_F(Create, eexist) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EEXIST))); + EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); + EXPECT_EQ(EEXIST, errno); +} + +/* + * If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback + * to FUSE_MKNOD/FUSE_OPEN + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236236 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Create, DISABLED_Enosys) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + int fd; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOSYS))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_mknod_in); + return (in->header.opcode == FUSE_MKNOD && + in->body.mknod.mode == (S_IFREG | mode) && + in->body.mknod.rdev == 0 && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = S_IFREG | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + }))); + + fd = open(FULLPATH, O_CREAT | O_EXCL, mode); + EXPECT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Creating a new file after FUSE_LOOKUP returned a negative cache entry + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Create, DISABLED_entry_cache_negative) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + int fd; + /* + * Set entry_valid = 0 because this test isn't concerned with whether + * or not we actually cache negative entries, only with whether we + * interpret negative cache responses correctly. + */ + struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; + + /* create will first do a LOOKUP, adding a negative cache entry */ + EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid)); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = S_IFREG | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + }))); + + fd = open(FULLPATH, O_CREAT | O_EXCL, mode); + ASSERT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Creating a new file should purge any negative namecache entries + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Create, DISABLED_entry_cache_negative_purge) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + int fd; + struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; + + /* create will first do a LOOKUP, adding a negative cache entry */ + EXPECT_LOOKUP(1, RELPATH).Times(1) + .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))) + .RetiresOnSaturation(); + + /* Then the CREATE should purge the negative cache entry */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = S_IFREG | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + }))); + + fd = open(FULLPATH, O_CREAT | O_EXCL, mode); + ASSERT_LE(0, fd) << strerror(errno); + + /* Finally, a subsequent lookup should query the daemon */ + expect_lookup(RELPATH, ino, S_IFREG | mode, 0, 1); + + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * The daemon is responsible for checking file permissions (unless the + * default_permissions mount option was used) + */ +TEST_F(Create, eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EPERM))); + EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); + EXPECT_EQ(EPERM, errno); +} + +TEST_F(Create, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + int fd; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_CREATE && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, create); + out->body.create.entry.attr.mode = S_IFREG | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + }))); + + fd = open(FULLPATH, O_CREAT | O_EXCL, mode); + EXPECT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/create.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/destroy.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/destroy.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/destroy.cc (revision 345356) @@ -0,0 +1,92 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Destroy: public FuseTest { +public: +void expect_destroy(int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_DESTROY); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +void expect_forget(uint64_t ino, uint64_t nlookup) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_FORGET && + in->header.nodeid == ino && + in->body.forget.nlookup == nlookup); + }, Eq(true)), + _) + ).WillOnce(Invoke([](auto in __unused, auto &out __unused) { + /* FUSE_FORGET has no response! */ + })); +} +}; + +/* + * On unmount the kernel should send a FUSE_DESTROY operation. It should also + * send FUSE_FORGET operations for all inodes with lookup_count > 0. It's hard + * to trigger FUSE_FORGET in way except by unmounting, so this is the only + * testing that FUSE_FORGET gets. + */ +TEST_F(Destroy, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); + expect_forget(1, 1); + expect_forget(ino, 2); + expect_destroy(0); + + /* + * access(2) the file to force a lookup. Access it twice to double its + * lookup count. + */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + + /* + * Unmount, triggering a FUSE_DESTROY and also causing a VOP_RECLAIM + * for every vnode on this mp, triggering FUSE_FORGET for each of them. + */ + m_mock->unmount(); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/destroy.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/flush.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/flush.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/flush.cc (revision 345356) @@ -0,0 +1,231 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Flush: public FuseTest { + +public: +void expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_FLUSH && + in->header.nodeid == ino && + in->body.flush.lock_owner == (uint64_t)lo && + in->body.flush.fh == FH); + }, Eq(true)), + _) + ).Times(times) + .WillRepeatedly(Invoke(r)); +} + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} + +/* + * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting. This + * expectation will silence googlemock warnings + */ +void expect_release() +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_RELEASE); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnErrno(0))); +} +}; + +class FlushWithLocks: public Flush { + virtual void SetUp() { + m_init_flags = FUSE_POSIX_LOCKS; + Flush::SetUp(); + } +}; + +/* If a file descriptor is duplicated, every close causes FLUSH */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ +TEST_F(Flush, DISABLED_dup) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd, fd2; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_flush(ino, 2, 0, ReturnErrno(0)); + expect_release(); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + fd2 = dup(fd); + + ASSERT_EQ(0, close(fd2)) << strerror(errno); + ASSERT_EQ(0, close(fd)) << strerror(errno); +} + +/* + * Some FUSE filesystem cache data internally and flush it on release. Such + * filesystems may generate errors during release. On Linux, these get + * returned by close(2). However, POSIX does not require close(2) to return + * this error. FreeBSD's fuse(4) should return EIO if it returns an error at + * all. + */ +/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ +TEST_F(Flush, DISABLED_eio) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_flush(ino, 1, 0, ReturnErrno(EIO)); + expect_release(); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); +} + +/* + * If the filesystem returns ENOSYS, it will be treated as success and + * no more FUSE_FLUSH operations will be sent to the daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Flush, DISABLED_enosys) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd, fd2; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + /* On the 2nd close, FUSE_FLUSH won't be sent at all */ + expect_flush(ino, 1, 0, ReturnErrno(ENOSYS)); + expect_release(); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + fd2 = dup(fd); + + EXPECT_EQ(0, close(fd2)) << strerror(errno); + EXPECT_EQ(0, close(fd)) << strerror(errno); +} + +/* A FUSE_FLUSH should be sent on close(2) */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ +TEST_F(Flush, DISABLED_flush) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_flush(ino, 1, 0, ReturnErrno(0)); + expect_release(); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_TRUE(0 == close(fd)) << strerror(errno); +} + +/* + * When closing a file with a POSIX file lock, flush should release the lock, + * _even_if_ it's not the process's last file descriptor for this file. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(FlushWithLocks, DISABLED_unlock_on_close) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd, fd2; + struct flock fl; + pid_t pid = getpid(); + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlk.fh == FH); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, setlk); + out->body.setlk.lk = in->body.setlk.lk; + }))); + expect_flush(ino, 1, pid, ReturnErrno(0)); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 0; + fl.l_len = 0; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); + + fd2 = dup(fd); + ASSERT_EQ(0, close(fd2)) << strerror(errno); + /* Deliberately leak fd */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/flush.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/fsync.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/fsync.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/fsync.cc (revision 345356) @@ -0,0 +1,313 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +/* + * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28. + * This bit was actually part of kernel protocol version 5.2, but never + * documented until after 7.28 + */ +#ifndef FUSE_FSYNC_FDATASYNC +#define FUSE_FSYNC_FDATASYNC 1 +#endif + +class Fsync: public FuseTest { +public: +void expect_fsync(uint64_t ino, uint32_t flags, int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_FSYNC && + in->header.nodeid == ino && + in->body.fsync.fh == FH && + in->body.fsync.fsync_flags == flags); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} + +void expect_write(uint64_t ino, uint64_t size, const void *contents) +{ + FuseTest::expect_write(ino, 0, size, size, 0, contents); +} + +}; + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(Fsync, DISABLED_aio_fsync) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + struct aiocb iocb, *piocb; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + expect_fsync(ino, 0, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + + bzero(&iocb, sizeof(iocb)); + iocb.aio_fildes = fd; + + ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno); + ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE + * + * This test only really make sense in writeback caching mode, but it should + * still pass in any cache mode. + */ +TEST_F(Fsync, close) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETATTR); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, attr); + out->body.attr.attr.ino = ino; // Must match nodeid + }))); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_FSYNC); + }, Eq(true)), + _) + ).Times(0); + expect_release(ino, 1, 0, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + close(fd); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(Fsync, DISABLED_eio) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + ASSERT_NE(0, fdatasync(fd)); + ASSERT_EQ(EIO, errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the filesystem returns ENOSYS, it will be treated as success and + * subsequent calls to VOP_FSYNC will succeed automatically without being sent + * to the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Fsync, DISABLED_enosys) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + EXPECT_EQ(0, fdatasync(fd)); + + /* Subsequent calls shouldn't query the daemon*/ + EXPECT_EQ(0, fdatasync(fd)); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(Fsync, DISABLED_fdatasync) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + ASSERT_EQ(0, fdatasync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(Fsync, DISABLED_fsync) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + ssize_t bufsize = strlen(CONTENTS); + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, bufsize, CONTENTS); + expect_fsync(ino, 0, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + ASSERT_EQ(0, fsync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Fsync should sync a file with dirty metadata but clean data */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(Fsync, DISABLED_fsync_metadata_only) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + mode_t mode = 0755; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETATTR); + }, 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 = S_IFREG | mode; + }))); + + expect_fsync(ino, 0, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, fchmod(fd, mode)) << strerror(errno); + ASSERT_EQ(0, fsync(fd)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +// fsync()ing a file that isn't dirty should be a no-op +TEST_F(Fsync, nop) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_WRONLY); + ASSERT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(0, fsync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +// TODO: ENOSYS test Property changes on: projects/fuse2/tests/sys/fs/fusefs/fsync.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/fsyncdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/fsyncdir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/fsyncdir.cc (revision 345356) @@ -0,0 +1,191 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +/* + * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28. + * This bit was actually part of kernel protocol version 5.2, but never + * documented until after 7.28 + */ +#ifndef FUSE_FSYNC_FDATASYNC +#define FUSE_FSYNC_FDATASYNC 1 +#endif + +class FsyncDir: public FuseTest { +public: +void expect_fsyncdir(uint64_t ino, uint32_t flags, int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_FSYNCDIR && + in->header.nodeid == ino && + //(pid_t)in->header.pid == getpid() && + in->body.fsyncdir.fh == FH && + in->body.fsyncdir.fsync_flags == flags); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); +} + +}; + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(FsyncDir, DISABLED_aio_fsync) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct aiocb iocb, *piocb; + int fd; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_fsyncdir(ino, 0, 0); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + + bzero(&iocb, sizeof(iocb)); + iocb.aio_fildes = fd; + + ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno); + ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(FsyncDir, DISABLED_eio) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_fsyncdir(ino, 0, EIO); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_NE(0, fsync(fd)); + ASSERT_EQ(EIO, errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the filesystem returns ENOSYS, it will be treated as success and + * subsequent calls to VOP_FSYNC will succeed automatically without being sent + * to the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(FsyncDir, DISABLED_enosys) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_fsyncdir(ino, FUSE_FSYNC_FDATASYNC, ENOSYS); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + EXPECT_EQ(0, fsync(fd)) << strerror(errno); + + /* Subsequent calls shouldn't query the daemon*/ + EXPECT_EQ(0, fsync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(FsyncDir, DISABLED_fsyncdata) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_fsyncdir(ino, FUSE_FSYNC_FDATASYNC, 0); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, fdatasync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Unlike regular files, the kernel doesn't know whether a directory is or + * isn't dirty, so fuse(4) should always send FUSE_FSYNCDIR on fsync(2) + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ +TEST_F(FsyncDir, DISABLED_fsync) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_fsyncdir(ino, 0, 0); + + fd = open(FULLPATH, O_DIRECTORY); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, fsync(fd)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/fsyncdir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/getattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/getattr.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/getattr.cc (revision 345356) @@ -0,0 +1,185 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Getattr : public FuseTest {}; + +/* + * If getattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs + * should use the cached attributes, rather than query the daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ +TEST_F(Getattr, DISABLED_attr_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + struct stat sb; + + EXPECT_LOOKUP(1, RELPATH) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFREG | 0644; + out->body.entry.nodeid = ino; + }))); + 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_valid = UINT64_MAX; + out->body.attr.attr.ino = ino; // Must match nodeid + out->body.attr.attr.mode = S_IFREG | 0644; + }))); + EXPECT_EQ(0, stat(FULLPATH, &sb)); + /* The second stat(2) should use cached attributes */ + EXPECT_EQ(0, stat(FULLPATH, &sb)); +} + +/* + * If getattr returns a finite but non-zero cache timeout, then we should + * discard the cached attributes and requery the daemon after the timeout + * period passes. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ +TEST_F(Getattr, attr_cache_timeout) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + struct stat sb; + /* + * The timeout should be longer than the longest plausible time the + * daemon would take to complete a write(2) to /dev/fuse, but no longer. + */ + long timeout_ns = 250'000'000; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).Times(2) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { + SET_OUT_HEADER_LEN(out, attr); + out->body.attr.attr_valid_nsec = timeout_ns; + out->body.attr.attr_valid = UINT64_MAX; + out->body.attr.attr.ino = ino; // Must match nodeid + out->body.attr.attr.mode = S_IFREG | 0644; + }))); + EXPECT_EQ(0, stat(FULLPATH, &sb)); + usleep(2 * timeout_ns / 1000); + /* Timeout has expire. stat(2) should requery the daemon */ + EXPECT_EQ(0, stat(FULLPATH, &sb)); +} + +TEST_F(Getattr, enoent) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + struct stat sb; + const uint64_t ino = 42; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOENT))); + EXPECT_NE(0, stat(FULLPATH, &sb)); + EXPECT_EQ(ENOENT, errno); +} + +TEST_F(Getattr, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + struct stat sb; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 1, 1); + 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 = S_IFREG | 0644; + out->body.attr.attr.size = 1; + out->body.attr.attr.blocks = 2; + out->body.attr.attr.atime = 3; + out->body.attr.attr.mtime = 4; + out->body.attr.attr.ctime = 5; + out->body.attr.attr.atimensec = 6; + out->body.attr.attr.mtimensec = 7; + out->body.attr.attr.ctimensec = 8; + out->body.attr.attr.nlink = 9; + out->body.attr.attr.uid = 10; + out->body.attr.attr.gid = 11; + out->body.attr.attr.rdev = 12; + }))); + + ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); + EXPECT_EQ(1, sb.st_size); + EXPECT_EQ(2, sb.st_blocks); + EXPECT_EQ(3, sb.st_atim.tv_sec); + EXPECT_EQ(6, sb.st_atim.tv_nsec); + EXPECT_EQ(4, sb.st_mtim.tv_sec); + EXPECT_EQ(7, sb.st_mtim.tv_nsec); + EXPECT_EQ(5, sb.st_ctim.tv_sec); + EXPECT_EQ(8, sb.st_ctim.tv_nsec); + EXPECT_EQ(9ull, sb.st_nlink); + EXPECT_EQ(10ul, sb.st_uid); + EXPECT_EQ(11ul, sb.st_gid); + EXPECT_EQ(12ul, sb.st_rdev); + EXPECT_EQ(ino, sb.st_ino); + EXPECT_EQ(S_IFREG | 0644, sb.st_mode); + + //st_birthtim and st_flags are not supported by protocol 7.8. They're + //only supported as OS-specific extensions to OSX. + //EXPECT_EQ(, sb.st_birthtim); + //EXPECT_EQ(, sb.st_flags); + + //FUSE can't set st_blksize until protocol 7.9 +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/getattr.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/interrupt.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/interrupt.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/interrupt.cc (revision 345356) @@ -0,0 +1,312 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +/* Don't do anything; all we care about is that the syscall gets interrupted */ +void sigusr2_handler(int __unused sig) { + if (verbosity > 1) + printf("Signaled!\n"); +} + +void* killer(void* target) { + /* + * Sleep for awhile so we can be mostly confident that the main thread + * is already blocked in write(2) + */ + usleep(250'000); + if (verbosity > 1) + printf("Signalling!\n"); + pthread_kill(*(pthread_t*)target, SIGUSR2); + + return(NULL); +} + +class Interrupt: public FuseTest { +public: +pthread_t m_child; + +Interrupt(): m_child(NULL) {}; + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} + +/* + * Expect a FUSE_WRITE but don't reply. Instead, just record the unique value + * to the provided pointer + */ +void expect_write(uint64_t ino, uint64_t *write_unique) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_WRITE && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke([=](auto in, auto &out __unused) { + *write_unique = in->header.unique; + })); +} + +void setup_interruptor(pthread_t self) +{ + ASSERT_EQ(0, signal(SIGUSR2, sigusr2_handler)) << strerror(errno); + ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)self)) + << strerror(errno); +} + +void TearDown() { + if (m_child != NULL) { + pthread_join(m_child, NULL); + } + + FuseTest::TearDown(); +} +}; + +/* + * An interrupt operation that gets received after the original command is + * complete should generate an EAGAIN response. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ +TEST_F(Interrupt, DISABLED_already_complete) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + pthread_t self; + uint64_t write_unique = 0; + + self = pthread_self(); + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, &write_unique); + EXPECT_CALL(*m_mock, process( + ResultOf([&](auto in) { + return (in->header.opcode == FUSE_INTERRUPT && + in->body.interrupt.unique == write_unique); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in, auto &out) { + // First complete the write request + auto out0 = new mockfs_buf_out; + out0->header.unique = write_unique; + SET_OUT_HEADER_LEN(out0, write); + out0->body.write.size = bufsize; + out.push_back(out0); + + // Then, respond EAGAIN to the interrupt request + auto out1 = new mockfs_buf_out; + out1->header.unique = in->header.unique; + out1->header.error = -EAGAIN; + out1->header.len = sizeof(out1->header); + out.push_back(out1); + })); + + fd = open(FULLPATH, O_WRONLY); + ASSERT_LE(0, fd) << strerror(errno); + + setup_interruptor(self); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and + * complete the original operation whenever it damn well pleases. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ +TEST_F(Interrupt, DISABLED_ignore) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + pthread_t self; + uint64_t write_unique; + + self = pthread_self(); + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, &write_unique); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_INTERRUPT && + in->body.interrupt.unique == write_unique); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in __unused, auto &out) { + // Ignore FUSE_INTERRUPT; respond to the FUSE_WRITE + auto out0 = new mockfs_buf_out; + out0->header.unique = write_unique; + SET_OUT_HEADER_LEN(out0, write); + out0->body.write.size = bufsize; + out.push_back(out0); + })); + + fd = open(FULLPATH, O_WRONLY); + ASSERT_LE(0, fd) << strerror(errno); + + setup_interruptor(self); + ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * A syscall that gets interrupted while blocking on FUSE I/O should send a + * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR + * in response to the _original_ operation. The kernel should ultimately + * return EINTR to userspace + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ +TEST_F(Interrupt, DISABLED_in_progress) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + pthread_t self; + uint64_t write_unique; + + self = pthread_self(); + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, &write_unique); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_INTERRUPT && + in->body.interrupt.unique == write_unique); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in __unused, auto &out) { + auto out0 = new mockfs_buf_out; + out0->header.error = -EINTR; + out0->header.unique = write_unique; + out0->header.len = sizeof(out0->header); + out.push_back(out0); + })); + + fd = open(FULLPATH, O_WRONLY); + ASSERT_LE(0, fd) << strerror(errno); + + setup_interruptor(self); + ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); + EXPECT_EQ(EINTR, errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the FUSE filesystem receives the FUSE_INTERRUPT operation before + * processing the original, then it should wait for "some timeout" for the + * original operation to arrive. If not, it should send EAGAIN to the + * INTERRUPT operation, and the kernel should requeue the INTERRUPT. + * + * In this test, we'll pretend that the INTERRUPT arrives too soon, gets + * EAGAINed, then the kernel requeues it, and the second time around it + * successfully interrupts the original + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ +TEST_F(Interrupt, DISABLED_too_soon) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const char *CONTENTS = "abcdefgh"; + uint64_t ino = 42; + int fd; + ssize_t bufsize = strlen(CONTENTS); + pthread_t self; + uint64_t write_unique; + + self = pthread_self(); + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_write(ino, &write_unique); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_INTERRUPT && + in->body.interrupt.unique == write_unique); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EAGAIN))) + .RetiresOnSaturation(); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_INTERRUPT && + in->body.interrupt.unique == write_unique); + }, Eq(true)), + _) + ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { + auto out0 = new mockfs_buf_out; + out0->header.error = -EINTR; + out0->header.unique = write_unique; + out0->header.len = sizeof(out0->header); + out.push_back(out0); + })); + + fd = open(FULLPATH, O_WRONLY); + ASSERT_LE(0, fd) << strerror(errno); + + setup_interruptor(self); + ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); + EXPECT_EQ(EINTR, errno); + + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/interrupt.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/link.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/link.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/link.cc (revision 345356) @@ -0,0 +1,102 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Link: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} +}; + +TEST_F(Link, emlink) +{ + const char FULLPATH[] = "mountpoint/lnk"; + const char RELPATH[] = "lnk"; + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + uint64_t dst_ino = 42; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + expect_lookup(RELDST, dst_ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(struct fuse_link_in); + return (in->header.opcode == FUSE_LINK && + in->body.link.oldnodeid == dst_ino && + (0 == strcmp(name, RELPATH))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EMLINK))); + + EXPECT_EQ(-1, link(FULLDST, FULLPATH)); + EXPECT_EQ(EMLINK, errno); +} + +TEST_F(Link, ok) +{ + const char FULLPATH[] = "mountpoint/src"; + const char RELPATH[] = "src"; + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + uint64_t dst_ino = 42; + const uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + expect_lookup(RELDST, dst_ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(struct fuse_link_in); + return (in->header.opcode == FUSE_LINK && + in->body.link.oldnodeid == dst_ino && + (0 == strcmp(name, RELPATH))); + }, Eq(true)), + _) + ).WillOnce(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; + }))); + + EXPECT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/link.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/locks.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/locks.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/locks.cc (revision 345356) @@ -0,0 +1,426 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +/* This flag value should probably be defined in fuse_kernel.h */ +#define OFFSET_MAX 0x7fffffffffffffffLL + +using namespace testing; + +/* For testing filesystems without posix locking support */ +class Fallback: public FuseTest { +public: + +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); +} + +}; + +/* For testing filesystems with posix locking support */ +class Locks: public Fallback { + virtual void SetUp() { + m_init_flags = FUSE_POSIX_LOCKS; + Fallback::SetUp(); + } +}; + +class GetlkFallback: public Fallback {}; +class Getlk: public Locks {}; +class SetlkFallback: public Fallback {}; +class Setlk: public Locks {}; +class SetlkwFallback: public Fallback {}; +class Setlkw: public Locks {}; + +/* + * If the fuse filesystem does not support posix file locks, then the kernel + * should fall back to local locks. + */ +TEST_F(GetlkFallback, local) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = getpid(); + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the filesystem has no locks that fit the description, the filesystem + * should return F_UNLCK + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Getlk, DISABLED_no_locks) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETLK && + in->header.nodeid == ino && + in->body.getlk.fh == FH && + in->body.getlk.owner == (uint32_t)pid && + in->body.getlk.lk.start == 10 && + in->body.getlk.lk.end == 1009 && + in->body.getlk.lk.type == F_RDLCK && + in->body.getlk.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, getlk); + out->body.getlk.lk = in->body.getlk.lk; + out->body.getlk.lk.type = F_UNLCK; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); + ASSERT_EQ(F_UNLCK, fl.l_type); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* A different pid does have a lock */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Getlk, DISABLED_lock_exists) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + pid_t pid2 = 1235; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETLK && + in->header.nodeid == ino && + in->body.getlk.fh == FH && + in->body.getlk.owner == (uint32_t)pid && + in->body.getlk.lk.start == 10 && + in->body.getlk.lk.end == 1009 && + in->body.getlk.lk.type == F_RDLCK && + in->body.getlk.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, getlk); + out->body.getlk.lk.start = 100; + out->body.getlk.lk.end = 199; + out->body.getlk.lk.type = F_WRLCK; + out->body.getlk.lk.pid = (uint32_t)pid2;; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); + EXPECT_EQ(100, fl.l_start); + EXPECT_EQ(100, fl.l_len); + EXPECT_EQ(pid2, fl.l_pid); + EXPECT_EQ(F_WRLCK, fl.l_type); + EXPECT_EQ(SEEK_SET, fl.l_whence); + EXPECT_EQ(0, fl.l_sysid); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the fuse filesystem does not support posix file locks, then the kernel + * should fall back to local locks. + */ +TEST_F(SetlkFallback, local) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = getpid(); + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Set a new lock with FUSE_SETLK */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Setlk, DISABLED_set) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlk.fh == FH && + in->body.setlk.owner == (uint32_t)pid && + in->body.setlk.lk.start == 10 && + in->body.setlk.lk.end == 1009 && + in->body.setlk.lk.type == F_RDLCK && + in->body.setlk.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, setlk); + out->body.setlk.lk = in->body.setlk.lk; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* l_len = 0 is a flag value that means to lock until EOF */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Setlk, DISABLED_set_eof) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlk.fh == FH && + in->body.setlk.owner == (uint32_t)pid && + in->body.setlk.lk.start == 10 && + in->body.setlk.lk.end == OFFSET_MAX && + in->body.setlk.lk.type == F_RDLCK && + in->body.setlk.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, setlk); + out->body.setlk.lk = in->body.setlk.lk; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 0; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Fail to set a new lock with FUSE_SETLK due to a conflict */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Setlk, DISABLED_eagain) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlk.fh == FH && + in->body.setlk.owner == (uint32_t)pid && + in->body.setlk.lk.start == 10 && + in->body.setlk.lk.end == 1009 && + in->body.setlk.lk.type == F_RDLCK && + in->body.setlk.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EAGAIN))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl)); + ASSERT_EQ(EAGAIN, errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * If the fuse filesystem does not support posix file locks, then the kernel + * should fall back to local locks. + */ +TEST_F(SetlkwFallback, local) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = getpid(); + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* + * Set a new lock with FUSE_SETLK. If the lock is not available, then the + * command should block. But to the kernel, that's the same as just being + * slow, so we don't need a separate test method + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(Setlkw, DISABLED_set) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + struct flock fl; + int fd; + pid_t pid = 1234; + + expect_lookup(RELPATH, ino); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlkw.fh == FH && + in->body.setlkw.owner == (uint32_t)pid && + in->body.setlkw.lk.start == 10 && + in->body.setlkw.lk.end == 1009 && + in->body.setlkw.lk.type == F_RDLCK && + in->body.setlkw.lk.pid == (uint64_t)pid); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, setlkw); + out->body.setlkw.lk = in->body.setlkw.lk; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 10; + fl.l_len = 1000; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/locks.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/mkdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/mkdir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/mkdir.cc (revision 345356) @@ -0,0 +1,178 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Mkdir: public FuseTest {}; + +/* + * EMLINK is possible on filesystems that limit the number of hard links to a + * single file, like early versions of BtrFS + */ +TEST_F(Mkdir, emlink) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + mode_t mode = 0755; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_mkdir_in); + return (in->header.opcode == FUSE_MKDIR && + in->body.mkdir.mode == (S_IFDIR | mode) && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EMLINK))); + + ASSERT_NE(1, mkdir(FULLPATH, mode)); + ASSERT_EQ(EMLINK, errno); +} + +/* + * Creating a new directory after FUSE_LOOKUP returned a negative cache entry + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Mkdir, DISABLED_entry_cache_negative) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + /* + * Set entry_valid = 0 because this test isn't concerned with whether + * or not we actually cache negative entries, only with whether we + * interpret negative cache responses correctly. + */ + struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; + + /* mkdir will first do a LOOKUP, adding a negative cache entry */ + EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid)); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_MKDIR && + in->body.mkdir.mode == (S_IFDIR | mode) && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.create.entry.attr.mode = S_IFDIR | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); +} + +/* + * Creating a new directory should purge any negative namecache entries + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Mkdir, DISABLED_entry_cache_negative_purge) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + mode_t mode = 0755; + uint64_t ino = 42; + struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; + + /* mkdir will first do a LOOKUP, adding a negative cache entry */ + EXPECT_LOOKUP(1, RELPATH).Times(1) + .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))) + .RetiresOnSaturation(); + + /* Then the MKDIR should purge the negative cache entry */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_open_in); + return (in->header.opcode == FUSE_MKDIR && + in->body.mkdir.mode == (S_IFDIR | mode) && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFDIR | mode; + out->body.entry.nodeid = ino; + out->body.entry.attr_valid = UINT64_MAX; + }))); + + ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); + + /* Finally, a subsequent lookup should query the daemon */ + expect_lookup(RELPATH, ino, S_IFDIR | mode, 0, 1); + + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); +} + +TEST_F(Mkdir, ok) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + mode_t mode = 0755; + uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes + + sizeof(fuse_mkdir_in); + return (in->header.opcode == FUSE_MKDIR && + in->body.mkdir.mode == (S_IFDIR | mode) && + (0 == strcmp(RELPATH, name))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.create.entry.attr.mode = S_IFDIR | mode; + out->body.create.entry.nodeid = ino; + out->body.create.entry.entry_valid = UINT64_MAX; + out->body.create.entry.attr_valid = UINT64_MAX; + }))); + + ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/mkdir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/open.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/open.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/open.cc (revision 345356) @@ -0,0 +1,185 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Open: public FuseTest { + +public: + +/* Test an OK open of a file with the given flags */ +void test_ok(int os_flags, int fuse_flags) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->body.open.flags == (uint32_t)fuse_flags && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0644; + }))); + + fd = open(FULLPATH, os_flags); + EXPECT_LE(0, fd) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} +}; + + +/* + * The fuse daemon fails the request with enoent. This usually indicates a + * race condition: some other FUSE client removed the file in between when the + * kernel checked for it with lookup and tried to open it + */ +TEST_F(Open, enoent) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOENT))); + EXPECT_NE(0, open(FULLPATH, O_RDONLY)); + EXPECT_EQ(ENOENT, errno); +} + +/* + * The daemon is responsible for checking file permissions (unless the + * default_permissions mount option was used) + */ +TEST_F(Open, eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EPERM))); + EXPECT_NE(0, open(FULLPATH, O_RDONLY)); + EXPECT_EQ(EPERM, errno); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ +TEST_F(Open, DISABLED_o_append) +{ + test_ok(O_WRONLY | O_APPEND, O_WRONLY | O_APPEND); +} + +/* The kernel is supposed to filter out this flag */ +TEST_F(Open, o_creat) +{ + test_ok(O_WRONLY | O_CREAT, O_WRONLY); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ +TEST_F(Open, DISABLED_o_direct) +{ + test_ok(O_WRONLY | O_DIRECT, O_WRONLY | O_DIRECT); +} + +/* The kernel is supposed to filter out this flag */ +TEST_F(Open, o_excl) +{ + test_ok(O_WRONLY | O_EXCL, O_WRONLY); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236329 */ +TEST_F(Open, DISABLED_o_exec) +{ + test_ok(O_EXEC, O_EXEC); +} + +/* The kernel is supposed to filter out this flag */ +TEST_F(Open, o_noctty) +{ + test_ok(O_WRONLY | O_NOCTTY, O_WRONLY); +} + +TEST_F(Open, o_rdonly) +{ + test_ok(O_RDONLY, O_RDONLY); +} + +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ +TEST_F(Open, DISABLED_o_trunc) +{ + test_ok(O_WRONLY | O_TRUNC, O_WRONLY | O_TRUNC); +} + +TEST_F(Open, o_wronly) +{ + test_ok(O_WRONLY, O_WRONLY); +} + +TEST_F(Open, o_rdwr) +{ + test_ok(O_RDWR, O_RDWR); +} + Property changes on: projects/fuse2/tests/sys/fs/fusefs/open.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/opendir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/opendir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/opendir.cc (revision 345356) @@ -0,0 +1,147 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Opendir: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); +} +}; + + +/* + * The fuse daemon fails the request with enoent. This usually indicates a + * race condition: some other FUSE client removed the file in between when the + * kernel checked for it with lookup and tried to open it + */ +TEST_F(Opendir, enoent) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPENDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOENT))); + EXPECT_NE(0, open(FULLPATH, O_DIRECTORY)); + EXPECT_EQ(ENOENT, errno); +} + +/* + * The daemon is responsible for checking file permissions (unless the + * default_permissions mount option was used) + */ +TEST_F(Opendir, eperm) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPENDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EPERM))); + + EXPECT_NE(0, open(FULLPATH, O_DIRECTORY)); + EXPECT_EQ(EPERM, errno); +} + +TEST_F(Opendir, open) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPENDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, open); + }))); + + EXPECT_LE(0, open(FULLPATH, O_DIRECTORY)) << strerror(errno); +} + +TEST_F(Opendir, opendir) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino); + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_STATFS); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, statfs); + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPENDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, open); + }))); + + errno = 0; + EXPECT_NE(NULL, opendir(FULLPATH)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/opendir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/readdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/readdir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/readdir.cc (revision 345356) @@ -0,0 +1,299 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; +using namespace std; + +class Readdir: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); +} + +void expect_readdir(uint64_t ino, uint64_t off, vector &ents) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READDIR && + in->header.nodeid == ino && + in->body.readdir.offset == off); + }, Eq(true)), + _) + ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) { + struct fuse_dirent *fde = (struct fuse_dirent*)out->body.bytes; + int i = 0; + + out->header.error = 0; + out->header.len = 0; + + for (const auto& it: ents) { + size_t entlen, entsize; + + fde->ino = it.d_fileno; + fde->off = it.d_off; + fde->type = it.d_type; + fde->namelen = it.d_namlen; + strncpy(fde->name, it.d_name, it.d_namlen); + entlen = FUSE_NAME_OFFSET + fde->namelen; + entsize = FUSE_DIRENT_SIZE(fde); + /* + * The FUSE protocol does not require zeroing out the + * unused portion of the name. But it's a good + * practice to prevent information disclosure to the + * FUSE client, even though the client is usually the + * kernel + */ + memset(fde->name + fde->namelen, 0, entsize - entlen); + if (out->header.len + entsize > in->body.read.size) { + printf("Overflow in readdir expectation: i=%d\n" + , i); + break; + } + out->header.len += entsize; + fde = (struct fuse_dirent*) + ((long*)fde + entsize / sizeof(long)); + i++; + } + out->header.len += sizeof(out->header); + }))); + +} +}; + +/* FUSE_READDIR returns nothing but "." and ".." */ +TEST_F(Readdir, dots) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + DIR *dir; + struct dirent *de; + vector ents(2); + vector empty_ents(0); + const char *dot = "."; + const char *dotdot = ".."; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + ents[0].d_fileno = 2; + ents[0].d_off = 2000; + ents[0].d_namlen = strlen(dotdot); + ents[0].d_type = DT_DIR; + strncpy(ents[0].d_name, dotdot, ents[0].d_namlen); + ents[1].d_fileno = 3; + ents[1].d_off = 3000; + ents[1].d_namlen = strlen(dot); + ents[1].d_type = DT_DIR; + strncpy(ents[1].d_name, dot, ents[1].d_namlen); + expect_readdir(ino, 0, ents); + expect_readdir(ino, 3000, empty_ents); + + errno = 0; + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + + errno = 0; + de = readdir(dir); + ASSERT_NE(NULL, de) << strerror(errno); + EXPECT_EQ(2ul, de->d_fileno); + /* + * fuse(4) doesn't actually set d_off, which is ok for now because + * nothing uses it. + */ + //EXPECT_EQ(2000, de->d_off); + EXPECT_EQ(DT_DIR, de->d_type); + EXPECT_EQ(2, de->d_namlen); + EXPECT_EQ(0, strcmp("..", de->d_name)); + + errno = 0; + de = readdir(dir); + ASSERT_NE(NULL, de) << strerror(errno); + EXPECT_EQ(3ul, de->d_fileno); + //EXPECT_EQ(3000, de->d_off); + EXPECT_EQ(DT_DIR, de->d_type); + EXPECT_EQ(1, de->d_namlen); + EXPECT_EQ(0, strcmp(".", de->d_name)); + + ASSERT_EQ(NULL, readdir(dir)); + ASSERT_EQ(0, errno); + + /* Deliberately leak dir. RELEASEDIR will be tested separately */ +} + +TEST_F(Readdir, eio) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + DIR *dir; + struct dirent *de; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READDIR && + in->header.nodeid == ino && + in->body.readdir.offset == 0); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EIO))); + + errno = 0; + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + + errno = 0; + de = readdir(dir); + ASSERT_EQ(NULL, de); + ASSERT_EQ(EIO, errno); + + /* Deliberately leak dir. RELEASEDIR will be tested separately */ +} + +/* + * FUSE_READDIR returns nothing, not even "." and "..". This is legal, though + * the filesystem obviously won't be fully functional. + */ +TEST_F(Readdir, nodots) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + DIR *dir; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READDIR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.error = 0; + out->header.len = sizeof(out->header); + }))); + + errno = 0; + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + errno = 0; + ASSERT_EQ(NULL, readdir(dir)); + ASSERT_EQ(0, errno); + + /* Deliberately leak dir. RELEASEDIR will be tested separately */ +} + +/* telldir(3) and seekdir(3) should work with fuse */ +TEST_F(Readdir, seekdir) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + DIR *dir; + struct dirent *de; + /* + * use enough entries to be > 4096 bytes, so getdirentries must be + * called + * multiple times. + */ + vector ents0(122), ents1(102), ents2(30); + long bookmark; + int i = 0; + + for (auto& it: ents0) { + snprintf(it.d_name, MAXNAMLEN, "file.%d", i); + it.d_fileno = 2 + i; + it.d_off = (2 + i) * 1000; + it.d_namlen = strlen(it.d_name); + it.d_type = DT_REG; + i++; + } + for (auto& it: ents1) { + snprintf(it.d_name, MAXNAMLEN, "file.%d", i); + it.d_fileno = 2 + i; + it.d_off = (2 + i) * 1000; + it.d_namlen = strlen(it.d_name); + it.d_type = DT_REG; + i++; + } + for (auto& it: ents2) { + snprintf(it.d_name, MAXNAMLEN, "file.%d", i); + it.d_fileno = 2 + i; + it.d_off = (2 + i) * 1000; + it.d_namlen = strlen(it.d_name); + it.d_type = DT_REG; + i++; + } + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + + expect_readdir(ino, 0, ents0); + expect_readdir(ino, 123000, ents1); + expect_readdir(ino, 225000, ents2); + + errno = 0; + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + + for (i=0; i < 128; i++) { + errno = 0; + de = readdir(dir); + ASSERT_NE(NULL, de) << strerror(errno); + EXPECT_EQ(2 + (ino_t)i, de->d_fileno); + } + bookmark = telldir(dir); + + for (; i < 232; i++) { + errno = 0; + de = readdir(dir); + ASSERT_NE(NULL, de) << strerror(errno); + EXPECT_EQ(2 + (ino_t)i, de->d_fileno); + } + + seekdir(dir, bookmark); + de = readdir(dir); + ASSERT_NE(NULL, de) << strerror(errno); + EXPECT_EQ(130ul, de->d_fileno); + + /* Deliberately leak dir. RELEASEDIR will be tested separately */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/readdir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/release.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/release.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/release.cc (revision 345356) @@ -0,0 +1,189 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Release: public FuseTest { + +public: +void expect_lookup(const char *relpath, uint64_t ino, int times) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); +} +}; + +class ReleaseWithLocks: public Release { + virtual void SetUp() { + m_init_flags = FUSE_POSIX_LOCKS; + Release::SetUp(); + } +}; + + +/* If a file descriptor is duplicated, only the last close causes RELEASE */ +TEST_F(Release, dup) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd, fd2; + + expect_lookup(RELPATH, ino, 1); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_release(ino, 1, 0, 0); + + fd = open(FULLPATH, O_RDONLY); + EXPECT_LE(0, fd) << strerror(errno); + + fd2 = dup(fd); + + ASSERT_EQ(0, close(fd2)) << strerror(errno); + ASSERT_EQ(0, close(fd)) << strerror(errno); +} + +/* + * Some FUSE filesystem cache data internally and flush it on release. Such + * filesystems may generate errors during release. On Linux, these get + * returned by close(2). However, POSIX does not require close(2) to return + * this error. FreeBSD's fuse(4) should return EIO if it returns an error at + * all. + */ +/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ +TEST_F(Release, eio) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino, 1); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_release(ino, 1, 0, EIO); + + fd = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); +} + +/* + * fuse(4) will issue multiple FUSE_OPEN operations for the same file if it's + * opened with different modes. Each FUSE_OPEN should get its own + * FUSE_RELEASE. + */ +TEST_F(Release, multiple_opens) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd, fd2; + + expect_lookup(RELPATH, ino, 2); + expect_open(ino, 0, 2); + expect_getattr(ino, 0); + expect_release(ino, 2, 0, 0); + + fd = open(FULLPATH, O_RDONLY); + EXPECT_LE(0, fd) << strerror(errno); + + fd2 = open(FULLPATH, O_WRONLY); + EXPECT_LE(0, fd2) << strerror(errno); + + ASSERT_EQ(0, close(fd2)) << strerror(errno); + ASSERT_EQ(0, close(fd)) << strerror(errno); +} + +TEST_F(Release, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino, 1); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_release(ino, 1, 0, 0); + + fd = open(FULLPATH, O_RDONLY); + EXPECT_LE(0, fd) << strerror(errno); + + ASSERT_EQ(0, close(fd)) << strerror(errno); +} + +/* When closing a file with a POSIX file lock, release should release the lock*/ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ +TEST_F(ReleaseWithLocks, DISABLED_unlock_on_close) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + struct flock fl; + pid_t pid = getpid(); + + expect_lookup(RELPATH, ino, 1); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_SETLK && + in->header.nodeid == ino && + in->body.setlk.fh == FH); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, setlk); + out->body.setlk.lk = in->body.setlk.lk; + }))); + expect_release(ino, 1, (uint64_t)pid, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + fl.l_start = 0; + fl.l_len = 0; + fl.l_pid = pid; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_sysid = 0; + ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); + + ASSERT_EQ(0, close(fd)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/release.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/releasedir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/releasedir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/releasedir.cc (revision 345356) @@ -0,0 +1,109 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class ReleaseDir: public FuseTest { + +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); +} + +void expect_releasedir(uint64_t ino, ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_RELEASEDIR && + in->header.nodeid == ino && + in->body.release.fh == FH); + }, Eq(true)), + _) + ).WillOnce(Invoke(r)); +} +}; + +/* If a file descriptor is duplicated, only the last close causes RELEASE */ +TEST_F(ReleaseDir, dup) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + DIR *dir, *dir2; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_READDIR && + in->header.nodeid == ino && + in->body.readdir.offset == 0); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.error = 0; + out->header.len = sizeof(out->header); + }))); + expect_releasedir(ino, ReturnErrno(0)); + + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + + dir2 = fdopendir(dup(dirfd(dir))); + ASSERT_NE(NULL, dir2) << strerror(errno); + + ASSERT_EQ(0, closedir(dir)) << strerror(errno); + ASSERT_EQ(0, closedir(dir2)) << strerror(errno); +} + +TEST_F(ReleaseDir, ok) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + DIR *dir; + + expect_lookup(RELPATH, ino); + expect_opendir(ino); + expect_releasedir(ino, ReturnErrno(0)); + + dir = opendir(FULLPATH); + ASSERT_NE(NULL, dir) << strerror(errno); + + ASSERT_EQ(0, closedir(dir)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/releasedir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/rename.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/rename.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/rename.cc (revision 345356) @@ -0,0 +1,249 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Rename: public FuseTest { + public: + int tmpfd = -1; + char tmpfile[80] = "/tmp/fuse.rename.XXXXXX"; + + virtual void TearDown() { + if (tmpfd >= 0) { + close(tmpfd); + unlink(tmpfile); + } + + FuseTest::TearDown(); + } +}; + +// EINVAL, dst is subdir of src +TEST_F(Rename, einval) +{ + const char FULLDST[] = "mountpoint/src/dst"; + const char RELDST[] = "dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + uint64_t src_ino = 42; + + expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 0, 2); + EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); + + ASSERT_NE(0, rename(FULLSRC, FULLDST)); + ASSERT_EQ(EINVAL, errno); +} + +// source does not exist +TEST_F(Rename, enoent) +{ + const char FULLDST[] = "mountpoint/dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + // FUSE hardcodes the mountpoint to inode 1 + + EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke(ReturnErrno(ENOENT))); + + ASSERT_NE(0, rename(FULLSRC, FULLDST)); + ASSERT_EQ(ENOENT, errno); +} + +/* + * Renaming a file after FUSE_LOOKUP returned a negative cache entry for dst + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Rename, DISABLED_entry_cache_negative) +{ + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + // FUSE hardcodes the mountpoint to inode 1 + uint64_t dst_dir_ino = 1; + uint64_t ino = 42; + /* + * Set entry_valid = 0 because this test isn't concerned with whether + * or not we actually cache negative entries, only with whether we + * interpret negative cache responses correctly. + */ + struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; + + expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); + /* LOOKUP returns a negative cache entry for dst */ + EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid)); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *src = (const char*)in->body.bytes + + sizeof(fuse_rename_in); + const char *dst = src + strlen(src) + 1; + return (in->header.opcode == FUSE_RENAME && + in->body.rename.newdir == dst_dir_ino && + (0 == strcmp(RELDST, dst)) && + (0 == strcmp(RELSRC, src))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(0))); + + ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); +} + +/* + * Renaming a file should purge any negative namecache entries for the dst + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236231 */ +TEST_F(Rename, DISABLED_entry_cache_negative_purge) +{ + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + // FUSE hardcodes the mountpoint to inode 1 + uint64_t dst_dir_ino = 1; + uint64_t ino = 42; + /* + * Set entry_valid = 0 because this test isn't concerned with whether + * or not we actually cache negative entries, only with whether we + * interpret negative cache responses correctly. + */ + struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; + + expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); + /* LOOKUP returns a negative cache entry for dst */ + EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid)) + .RetiresOnSaturation(); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *src = (const char*)in->body.bytes + + sizeof(fuse_rename_in); + const char *dst = src + strlen(src) + 1; + return (in->header.opcode == FUSE_RENAME && + in->body.rename.newdir == dst_dir_ino && + (0 == strcmp(RELDST, dst)) && + (0 == strcmp(RELSRC, src))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(0))); + + ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); + + /* Finally, a subsequent lookup should query the daemon */ + expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); + + ASSERT_EQ(0, access(FULLDST, F_OK)) << strerror(errno); +} + +TEST_F(Rename, exdev) +{ + const char FULLB[] = "mountpoint/src"; + const char RELB[] = "src"; + // FUSE hardcodes the mountpoint to inode 1 + uint64_t b_ino = 42; + + tmpfd = mkstemp(tmpfile); + ASSERT_LE(0, tmpfd) << strerror(errno); + + expect_lookup(RELB, b_ino, S_IFREG | 0644, 0, 2); + + ASSERT_NE(0, rename(tmpfile, FULLB)); + ASSERT_EQ(EXDEV, errno); + + ASSERT_NE(0, rename(FULLB, tmpfile)); + ASSERT_EQ(EXDEV, errno); +} + +TEST_F(Rename, ok) +{ + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + // FUSE hardcodes the mountpoint to inode 1 + uint64_t dst_dir_ino = 1; + uint64_t ino = 42; + + expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); + EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *src = (const char*)in->body.bytes + + sizeof(fuse_rename_in); + const char *dst = src + strlen(src) + 1; + return (in->header.opcode == FUSE_RENAME && + in->body.rename.newdir == dst_dir_ino && + (0 == strcmp(RELDST, dst)) && + (0 == strcmp(RELSRC, src))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(0))); + + ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); +} + +// Rename overwrites an existing destination file +TEST_F(Rename, overwrite) +{ + const char FULLDST[] = "mountpoint/dst"; + const char RELDST[] = "dst"; + const char FULLSRC[] = "mountpoint/src"; + const char RELSRC[] = "src"; + // The inode of the already-existing destination file + uint64_t dst_ino = 2; + // FUSE hardcodes the mountpoint to inode 1 + uint64_t dst_dir_ino = 1; + uint64_t ino = 42; + + expect_lookup(RELSRC, ino, S_IFREG | 0644, 0, 1); + expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 0, 1); + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *src = (const char*)in->body.bytes + + sizeof(fuse_rename_in); + const char *dst = src + strlen(src) + 1; + return (in->header.opcode == FUSE_RENAME && + in->body.rename.newdir == dst_dir_ino && + (0 == strcmp(RELDST, dst)) && + (0 == strcmp(RELSRC, src))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(0))); + + ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/rename.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/unlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/unlink.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/unlink.cc (revision 345356) @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2019 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Unlink: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino, int times) +{ + FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); +} + +void expect_unlink(uint64_t parent, const char *path, int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_UNLINK && + 0 == strcmp(path, in->body.unlink) && + in->header.nodeid == parent); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +}; + +TEST_F(Unlink, eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, 1); + expect_unlink(1, RELPATH, EPERM); + + ASSERT_NE(0, unlink(FULLPATH)); + ASSERT_EQ(EPERM, errno); +} + +TEST_F(Unlink, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + + expect_lookup(RELPATH, ino, 1); + expect_unlink(1, RELPATH, 0); + + ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); +} + +/* Unlink an open file */ +TEST_F(Unlink, open_but_deleted) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + + expect_lookup(RELPATH, ino, 2); + expect_open(ino, 0, 1); + expect_getattr(ino, 0); + expect_unlink(1, RELPATH, 0); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/unlink.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/xattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/xattr.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/xattr.cc (revision 345356) @@ -0,0 +1,605 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +const char FULLPATH[] = "mountpoint/some_file.txt"; +const char RELPATH[] = "some_file.txt"; + +/* For testing filesystems without posix locking support */ +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) +{ + 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)), + _) + ).WillOnce(Invoke(r)) + .RetiresOnSaturation(); +} + +void expect_removexattr(uint64_t ino, const char *attr, int error) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *a = (const char*)in->body.bytes; + return (in->header.opcode == FUSE_REMOVEXATTR && + in->header.nodeid == ino && + 0 == strcmp(attr, a)); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(error))); +} + +void expect_setxattr(uint64_t ino, const char *attr, const char *value, + ProcessMockerT r) +{ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *a = (const char*)in->body.bytes + + sizeof(fuse_setxattr_in); + const char *v = a + strlen(a) + 1; + return (in->header.opcode == FUSE_SETXATTR && + in->header.nodeid == ino && + 0 == strcmp(attr, a) && + 0 == strcmp(value, v)); + }, Eq(true)), + _) + ).WillOnce(Invoke(r)); +} + +}; + +class Getxattr: public Xattr {}; +class Listxattr: public Xattr {}; +class Removexattr: public Xattr {}; +class Setxattr: public Xattr {}; + +/* + * If the extended attribute does not exist on this file, the daemon should + * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the + * correct errror code) + */ +TEST_F(Getxattr, enoattr) +{ + char data[80]; + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + 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); +} + +/* + * If the filesystem returns ENOSYS, then it will be treated as a permanent + * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP + * without querying the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Getxattr, DISABLED_enosys) +{ + char data[80]; + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS)); + + r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); + ASSERT_EQ(-1, r); + EXPECT_EQ(EOPNOTSUPP, errno); + + /* Subsequent attempts should not query the filesystem at all */ + r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); + ASSERT_EQ(-1, r); + EXPECT_EQ(EOPNOTSUPP, errno); +} + +/* + * On FreeBSD, if the user passes an insufficiently large buffer then the + * filesystem is supposed to copy as much of the attribute's value as will fit. + * + * On Linux, however, the filesystem is supposed to return ERANGE. + * + * libfuse specifies the Linux behavior. However, that's probably an error. + * 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(Getxattr, erange) +{ + char data[10]; + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE)); + + r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); + ASSERT_EQ(-1, r); + ASSERT_EQ(ERANGE, errno); +} + +/* + * If the user passes a 0-length buffer, then the daemon should just return the + * size of the attribute + */ +TEST_F(Getxattr, size_only) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_getxattr(ino, "user.foo", + ReturnImmediate([](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, getxattr); + out->body.getxattr.size = 99; + }) + ); + + ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0)) + << strerror(errno);; +} + +/* + * Successfully get an attribute from the system namespace + */ +TEST_F(Getxattr, system) +{ + uint64_t ino = 42; + char data[80]; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_SYSTEM; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_getxattr(ino, "system.foo", + ReturnImmediate([&](auto in __unused, auto out) { + memcpy((void*)out->body.bytes, value, value_len); + out->header.len = sizeof(out->header) + value_len; + }) + ); + + r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); + ASSERT_EQ(value_len, r) << strerror(errno); + EXPECT_STREQ(value, data); +} + +/* + * Successfully get an attribute from the user namespace + */ +TEST_F(Getxattr, user) +{ + uint64_t ino = 42; + char data[80]; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_getxattr(ino, "user.foo", + ReturnImmediate([&](auto in __unused, auto out) { + memcpy((void*)out->body.bytes, value, value_len); + out->header.len = sizeof(out->header) + value_len; + }) + ); + + r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); + ASSERT_EQ(value_len, r) << strerror(errno); + EXPECT_STREQ(value, data); +} + +/* + * If the filesystem returns ENOSYS, then it will be treated as a permanent + * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP + * without querying the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Listxattr, DISABLED_enosys) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnErrno(ENOSYS)); + + ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); + EXPECT_EQ(EOPNOTSUPP, errno); + + /* Subsequent attempts should not query the filesystem at all */ + ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); + EXPECT_EQ(EOPNOTSUPP, errno); +} + +/* + * Listing extended attributes failed because they aren't configured on this + * filesystem + */ +TEST_F(Listxattr, enotsup) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); + + ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); + ASSERT_EQ(ENOTSUP, errno); +} + +/* + * On FreeBSD, if the user passes an insufficiently large buffer then the + * filesystem is supposed to copy as much of the attribute's value as will fit. + * + * On Linux, however, the filesystem is supposed to return ERANGE. + * + * libfuse specifies the Linux behavior. However, that's probably an error. + * 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) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnErrno(ERANGE)); + + ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); + ASSERT_EQ(ERANGE, errno); +} + +/* + * Get the size of the list that it would take to list no extended attributes + */ +TEST_F(Listxattr, size_only_empty) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { + out->body.listxattr.size = 0; + SET_OUT_HEADER_LEN(out, listxattr); + })); + + ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) + << strerror(errno); +} + +/* + * Get the size of the list that it would take to list some extended + * attributes. Due to the format differences between a FreeBSD and a + * 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. + */ +TEST_F(Listxattr, size_only_nonempty) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { + out->body.listxattr.size = 45; + SET_OUT_HEADER_LEN(out, listxattr); + })); + + // TODO: fix the expected size after fixing the size calculation bug in + // fuse_vnop_listextattr. It should be exactly 45. + expect_listxattr(ino, 53, + ReturnImmediate([](auto in __unused, auto out) { + const char l[] = "user.foo"; + 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)) + << strerror(errno); +} + +TEST_F(Listxattr, size_only_really_big) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto out) { + out->body.listxattr.size = 16000; + SET_OUT_HEADER_LEN(out, listxattr); + })); + + // TODO: fix the expected size after fixing the size calculation bug in + // fuse_vnop_listextattr. It should be exactly 16000. + expect_listxattr(ino, 16008, + ReturnImmediate([](auto in __unused, auto out) { + const char l[16] = "user.foobarbang"; + for (int i=0; i < 1000; i++) { + memcpy(&out->body.bytes[16 * i], l, 16); + } + out->header.len = sizeof(fuse_out_header) + 16000; + }) + ); + + ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0)) + << strerror(errno); +} + +/* + * List all of the user attributes of a file which has both user and system + * attributes + */ +TEST_F(Listxattr, user) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + char data[80]; + char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'}; + char attrs[28] = "user.foo\0system.x\0user.bang"; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, + ReturnImmediate([&](auto in __unused, auto out) { + out->body.listxattr.size = sizeof(attrs); + SET_OUT_HEADER_LEN(out, listxattr); + }) + ); + + // TODO: fix the expected size after fixing the size calculation bug in + // fuse_vnop_listextattr. + expect_listxattr(ino, sizeof(attrs) + 8, + 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((ssize_t)sizeof(expected), + extattr_list_file(FULLPATH, ns, data, sizeof(data))) + << strerror(errno); + ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); +} + +/* + * List all of the system attributes of a file which has both user and system + * attributes + */ +TEST_F(Listxattr, system) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_SYSTEM; + char data[80]; + char expected[2] = {1, 'x'}; + char attrs[28] = "user.foo\0system.x\0user.bang"; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_listxattr(ino, 0, + ReturnImmediate([&](auto in __unused, auto out) { + out->body.listxattr.size = sizeof(attrs); + SET_OUT_HEADER_LEN(out, listxattr); + }) + ); + + // TODO: fix the expected size after fixing the size calculation bug in + // fuse_vnop_listextattr. + expect_listxattr(ino, sizeof(attrs) + 8, + 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((ssize_t)sizeof(expected), + extattr_list_file(FULLPATH, ns, data, sizeof(data))) + << strerror(errno); + ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); +} + +/* Fail to remove a nonexistent attribute */ +TEST_F(Removexattr, enoattr) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_removexattr(ino, "user.foo", ENOATTR); + + ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); + ASSERT_EQ(ENOATTR, errno); +} + +/* + * If the filesystem returns ENOSYS, then it will be treated as a permanent + * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP + * without querying the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Removexattr, DISABLED_enosys) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_removexattr(ino, "user.foo", ENOSYS); + + ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); + EXPECT_EQ(EOPNOTSUPP, errno); + + /* Subsequent attempts should not query the filesystem at all */ + ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); + EXPECT_EQ(EOPNOTSUPP, errno); +} + +/* Successfully remove a user xattr */ +TEST_F(Removexattr, user) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_USER; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_removexattr(ino, "user.foo", 0); + + ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) + << strerror(errno); +} + +/* Successfully remove a system xattr */ +TEST_F(Removexattr, system) +{ + uint64_t ino = 42; + int ns = EXTATTR_NAMESPACE_SYSTEM; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_removexattr(ino, "system.foo", 0); + + ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) + << strerror(errno); +} + +/* + * If the filesystem returns ENOSYS, then it will be treated as a permanent + * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP + * without querying the filesystem daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ +TEST_F(Setxattr, DISABLED_enosys) +{ + uint64_t ino = 42; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS)); + + r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); + ASSERT_EQ(-1, r); + EXPECT_EQ(EOPNOTSUPP, errno); + + /* Subsequent attempts should not query the filesystem at all */ + r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); + ASSERT_EQ(-1, r); + EXPECT_EQ(EOPNOTSUPP, errno); +} + +/* + * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem + * as currently configured doesn't support extended attributes. + */ +TEST_F(Setxattr, enotsup) +{ + uint64_t ino = 42; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP)); + + r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); + ASSERT_EQ(-1, r); + EXPECT_EQ(ENOTSUP, errno); +} + +/* + * Successfully set a user attribute. + */ +TEST_F(Setxattr, user) +{ + uint64_t ino = 42; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_USER; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_setxattr(ino, "user.foo", value, ReturnErrno(0)); + + r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); + ASSERT_EQ(value_len, r) << strerror(errno); +} + +/* + * Successfully set a system attribute. + */ +TEST_F(Setxattr, system) +{ + uint64_t ino = 42; + const char value[] = "whatever"; + ssize_t value_len = strlen(value) + 1; + int ns = EXTATTR_NAMESPACE_SYSTEM; + ssize_t r; + + expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); + expect_setxattr(ino, "system.foo", value, ReturnErrno(0)); + + r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); + ASSERT_EQ(value_len, r) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/xattr.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/lookup.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/lookup.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/lookup.cc (revision 345356) @@ -0,0 +1,291 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Lookup: public FuseTest {}; + +/* + * If lookup returns a non-zero cache timeout, then subsequent VOP_GETATTRs + * should use the cached attributes, rather than query the daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ +TEST_F(Lookup, DISABLED_attr_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const uint64_t generation = 13; + struct stat sb; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.nodeid = ino; + out->body.entry.attr_valid = UINT64_MAX; + out->body.entry.attr.ino = ino; // Must match nodeid + out->body.entry.attr.mode = S_IFREG | 0644; + out->body.entry.attr.size = 1; + out->body.entry.attr.blocks = 2; + out->body.entry.attr.atime = 3; + out->body.entry.attr.mtime = 4; + out->body.entry.attr.ctime = 5; + out->body.entry.attr.atimensec = 6; + out->body.entry.attr.mtimensec = 7; + out->body.entry.attr.ctimensec = 8; + out->body.entry.attr.nlink = 9; + out->body.entry.attr.uid = 10; + out->body.entry.attr.gid = 11; + out->body.entry.attr.rdev = 12; + out->body.entry.generation = generation; + }))); + /* stat(2) issues a VOP_LOOKUP followed by a VOP_GETATTR */ + ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); + EXPECT_EQ(1, sb.st_size); + EXPECT_EQ(2, sb.st_blocks); + EXPECT_EQ(3, sb.st_atim.tv_sec); + EXPECT_EQ(6, sb.st_atim.tv_nsec); + EXPECT_EQ(4, sb.st_mtim.tv_sec); + EXPECT_EQ(7, sb.st_mtim.tv_nsec); + EXPECT_EQ(5, sb.st_ctim.tv_sec); + EXPECT_EQ(8, sb.st_ctim.tv_nsec); + EXPECT_EQ(9ull, sb.st_nlink); + EXPECT_EQ(10ul, sb.st_uid); + EXPECT_EQ(11ul, sb.st_gid); + EXPECT_EQ(12ul, sb.st_rdev); + EXPECT_EQ(ino, sb.st_ino); + EXPECT_EQ(S_IFREG | 0644, sb.st_mode); + + // fuse(4) does not _yet_ support inode generations + //EXPECT_EQ(generation, sb.st_gen); + + //st_birthtim and st_flags are not supported by protocol 7.8. They're + //only supported as OS-specific extensions to OSX. + //EXPECT_EQ(, sb.st_birthtim); + //EXPECT_EQ(, sb.st_flags); + + //FUSE can't set st_blksize until protocol 7.9 +} + +/* + * If lookup returns a finite but non-zero cache timeout, then we should discard + * the cached attributes and requery the daemon. + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ +TEST_F(Lookup, attr_cache_timeout) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + struct stat sb; + /* + * The timeout should be longer than the longest plausible time the + * daemon would take to complete a write(2) to /dev/fuse, but no longer. + */ + long timeout_ns = 250'000'000; + + EXPECT_LOOKUP(1, RELPATH) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.nodeid = ino; + out->body.entry.attr_valid_nsec = timeout_ns; + out->body.entry.attr.ino = ino; // Must match nodeid + out->body.entry.attr.mode = S_IFREG | 0644; + }))); + expect_getattr(ino, 0); + + /* access(2) will issue a VOP_LOOKUP but not a VOP_GETATTR */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + usleep(2 * timeout_ns / 1000); + /* The cache has timed out; VOP_GETATTR should query the daemon*/ + ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno); +} + +TEST_F(Lookup, enoent) +{ + const char FULLPATH[] = "mountpoint/does_not_exist"; + const char RELPATH[] = "does_not_exist"; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + EXPECT_NE(0, access(FULLPATH, F_OK)); + EXPECT_EQ(ENOENT, errno); +} + +/* + * If lookup returns a non-zero entry timeout, then subsequent VOP_LOOKUPs + * should use the cached inode rather than requery the daemon + */ +TEST_F(Lookup, entry_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.entry_valid = UINT64_MAX; + out->body.entry.attr.mode = S_IFREG | 0644; + out->body.entry.nodeid = 14; + }))); + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + /* The second access(2) should use the cache */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); +} + +/* + * If the daemon returns an error of 0 and an inode of 0, that's a flag for + * "ENOENT and cache it" with the given entry_timeout + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236226 */ +TEST_F(Lookup, DISABLED_entry_cache_negative) +{ + struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; + + EXPECT_LOOKUP(1, "does_not_exist").Times(1) + .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))); + + EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK)); + EXPECT_EQ(ENOENT, errno); + EXPECT_NE(0, access("mountpoint/does_not_exist", F_OK)); + EXPECT_EQ(ENOENT, errno); +} + +/* Negative entry caches should timeout, too */ +TEST_F(Lookup, entry_cache_negative_timeout) +{ + const char *RELPATH = "does_not_exist"; + const char *FULLPATH = "mountpoint/does_not_exist"; + /* + * The timeout should be longer than the longest plausible time the + * daemon would take to complete a write(2) to /dev/fuse, but no longer. + */ + struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 250'000'000}; + + EXPECT_LOOKUP(1, RELPATH).Times(2) + .WillRepeatedly(Invoke(ReturnNegativeCache(&entry_valid))); + + EXPECT_NE(0, access(FULLPATH, F_OK)); + EXPECT_EQ(ENOENT, errno); + + usleep(2 * entry_valid.tv_nsec / 1000); + + /* The cache has timed out; VOP_LOOKUP should requery the daemon*/ + EXPECT_NE(0, access(FULLPATH, F_OK)); + EXPECT_EQ(ENOENT, errno); +} + +/* + * If lookup returns a finite but non-zero entry cache timeout, then we should + * discard the cached inode and requery the daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235773 */ +TEST_F(Lookup, DISABLED_entry_cache_timeout) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + /* + * The timeout should be longer than the longest plausible time the + * daemon would take to complete a write(2) to /dev/fuse, but no longer. + */ + long timeout_ns = 250'000'000; + + EXPECT_LOOKUP(1, RELPATH).Times(2) + .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.entry_valid_nsec = timeout_ns; + out->body.entry.attr.mode = S_IFREG | 0644; + out->body.entry.nodeid = 14; + }))); + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); + usleep(2 * timeout_ns / 1000); + /* The cache has timed out; VOP_LOOKUP should query the daemon*/ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); +} + +// TODO: export_support +// After upgrading the protocol to 7.10, check that the kernel will only +// attempt to lookup "." and ".." if the filesystem sets FUSE_EXPORT_SUPPORT in +// the init flags. If not, then all lookups for those entries will return +// ESTALE. + +TEST_F(Lookup, ok) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(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 = 14; + }))); + /* + * access(2) is one of the few syscalls that will not (always) follow + * up a successful VOP_LOOKUP with another VOP. + */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); +} + +// Lookup in a subdirectory of the fuse mount +TEST_F(Lookup, subdir) +{ + const char FULLPATH[] = "mountpoint/some_dir/some_file.txt"; + const char DIRPATH[] = "some_dir"; + const char RELPATH[] = "some_file.txt"; + uint64_t dir_ino = 2; + uint64_t file_ino = 3; + + EXPECT_LOOKUP(1, DIRPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFDIR | 0755; + out->body.entry.nodeid = dir_ino; + }))); + EXPECT_LOOKUP(dir_ino, RELPATH) + .WillOnce(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 = file_ino; + }))); + /* + * access(2) is one of the few syscalls that will not (always) follow + * up a successful VOP_LOOKUP with another VOP. + */ + ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); +} + + Property changes on: projects/fuse2/tests/sys/fs/fusefs/lookup.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/rmdir.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/rmdir.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/rmdir.cc (revision 345356) @@ -0,0 +1,91 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Rmdir: public FuseTest { +public: +void expect_lookup(const char *relpath, uint64_t ino) +{ + EXPECT_LOOKUP(1, relpath) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFDIR | 0755; + out->body.entry.nodeid = ino; + out->body.entry.attr.nlink = 2; + }))); +} +}; + +TEST_F(Rmdir, enotempty) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + 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 == 1); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOTEMPTY))); + + ASSERT_NE(0, rmdir(FULLPATH)); + ASSERT_EQ(ENOTEMPTY, errno); +} + +TEST_F(Rmdir, ok) +{ + const char FULLPATH[] = "mountpoint/some_dir"; + const char RELPATH[] = "some_dir"; + uint64_t ino = 42; + + 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 == 1); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(0))); + + ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/rmdir.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/setattr.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/setattr.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/setattr.cc (revision 345356) @@ -0,0 +1,512 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include + +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Setattr : public FuseTest {}; + + +/* + * If setattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs + * should use the cached attributes, rather than query the daemon + */ +/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=235775 */ +TEST_F(Setattr, DISABLED_attr_cache) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + struct stat sb; + const mode_t newmode = 0644; + + EXPECT_LOOKUP(1, 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; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + /* In protocol 7.23, ctime will be changed too */ + 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_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_GETATTR); + }, Eq(true)), + _) + ).Times(0); + + /* Set an attribute with SETATTR */ + ASSERT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno); + + /* The stat(2) should use cached attributes */ + ASSERT_EQ(0, stat(FULLPATH, &sb)); + EXPECT_EQ(S_IFREG | newmode, sb.st_mode); +} + +/* Change the mode of a file */ +TEST_F(Setattr, chmod) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const mode_t oldmode = 0755; + const mode_t newmode = 0644; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFREG | oldmode; + out->body.entry.nodeid = ino; + out->body.entry.attr.mode = S_IFREG | oldmode; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_MODE; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.mode == newmode); + }, 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); +} + +/* Change the owner and group of a file */ +TEST_F(Setattr, chown) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const gid_t oldgroup = 66; + const gid_t newgroup = 99; + const uid_t olduser = 33; + const uid_t newuser = 44; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(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.gid = oldgroup; + out->body.entry.attr.uid = olduser; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_GID | FATTR_UID; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.uid == newuser && + in->body.setattr.gid == newgroup); + }, 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 | 0644; + out->body.attr.attr.uid = newuser; + out->body.attr.attr.gid = newgroup; + }))); + EXPECT_EQ(0, chown(FULLPATH, newuser, newgroup)) << strerror(errno); +} + + + +/* + * FUSE daemons are allowed to check permissions however they like. If the + * daemon returns EPERM, even if the file permissions "should" grant access, + * then fuse(4) should return EPERM too. + */ +TEST_F(Setattr, eperm) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFREG | 0777; + out->body.entry.nodeid = ino; + out->body.entry.attr.uid = in->header.uid; + out->body.entry.attr.gid = in->header.gid; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EPERM))); + EXPECT_NE(0, truncate(FULLPATH, 10)); + EXPECT_EQ(EPERM, errno); +} + +/* Change the mode of an open file, by its file descriptor */ +TEST_F(Setattr, fchmod) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + const mode_t oldmode = 0755; + const mode_t newmode = 0644; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFREG | oldmode; + out->body.entry.nodeid = ino; + out->body.entry.attr_valid = UINT64_MAX; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | oldmode; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_MODE; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.mode == newmode); + }, 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; + }))); + + fd = open(FULLPATH, O_RDONLY); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Change the size of an open file, by its file descriptor */ +TEST_F(Setattr, ftruncate) +{ + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + uint64_t ino = 42; + int fd; + uint64_t fh = 0xdeadbeef1a7ebabe; + const off_t oldsize = 99; + const off_t newsize = 12345; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFREG | 0755; + out->body.entry.nodeid = ino; + out->body.entry.attr_valid = UINT64_MAX; + out->body.entry.attr.size = oldsize; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_OPEN && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + out->header.len = sizeof(out->header); + SET_OUT_HEADER_LEN(out, open); + out->body.open.fh = fh; + }))); + + /* Until the attr cache is working, we may send an additional GETATTR */ + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + return (in->header.opcode == FUSE_GETATTR && + in->header.nodeid == ino); + }, Eq(true)), + _) + ).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.mode = S_IFREG | 0755; + out->body.attr.attr.size = oldsize; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_SIZE | FATTR_FH; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.fh == fh); + }, 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 | 0755; + out->body.attr.attr.size = newsize; + }))); + + fd = open(FULLPATH, O_RDWR); + ASSERT_LE(0, fd) << strerror(errno); + ASSERT_EQ(0, ftruncate(fd, newsize)) << strerror(errno); + /* Deliberately leak fd. close(2) will be tested in release.cc */ +} + +/* Change the size of the file */ +TEST_F(Setattr, truncate) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const uint64_t oldsize = 100'000'000; + const uint64_t newsize = 20'000'000; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(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.size = oldsize; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_SIZE; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.size == newsize); + }, 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 | 0644; + out->body.attr.attr.size = newsize; + }))); + EXPECT_EQ(0, truncate(FULLPATH, newsize)) << strerror(errno); +} + +/* Change a file's timestamps */ +TEST_F(Setattr, utimensat) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const timespec oldtimes[2] = { + {.tv_sec = 1, .tv_nsec = 2}, + {.tv_sec = 3, .tv_nsec = 4}, + }; + const timespec newtimes[2] = { + {.tv_sec = 5, .tv_nsec = 6}, + {.tv_sec = 7, .tv_nsec = 8}, + }; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(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_valid = UINT64_MAX; + out->body.entry.attr.atime = oldtimes[0].tv_sec; + out->body.entry.attr.atimensec = oldtimes[0].tv_nsec; + out->body.entry.attr.mtime = oldtimes[1].tv_sec; + out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec; + }))); + + /* + * Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR + * call + */ + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_GETATTR && + 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 | 0644; + out->body.attr.attr.atime = oldtimes[0].tv_sec; + out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; + out->body.attr.attr.mtime = oldtimes[1].tv_sec; + out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_ATIME | FATTR_MTIME; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.atime == newtimes[0].tv_sec && + in->body.setattr.atimensec == + newtimes[0].tv_nsec && + in->body.setattr.mtime == newtimes[1].tv_sec && + in->body.setattr.mtimensec == + newtimes[1].tv_nsec); + }, 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 | 0644; + out->body.attr.attr.atime = newtimes[0].tv_sec; + out->body.attr.attr.atimensec = newtimes[0].tv_nsec; + out->body.attr.attr.mtime = newtimes[1].tv_sec; + out->body.attr.attr.mtimensec = newtimes[1].tv_nsec; + }))); + EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0)) + << strerror(errno); +} + +/* Change a file mtime but not its atime */ +TEST_F(Setattr, utimensat_mtime_only) { + const char FULLPATH[] = "mountpoint/some_file.txt"; + const char RELPATH[] = "some_file.txt"; + const uint64_t ino = 42; + const timespec oldtimes[2] = { + {.tv_sec = 1, .tv_nsec = 2}, + {.tv_sec = 3, .tv_nsec = 4}, + }; + const timespec newtimes[2] = { + {.tv_sec = 5, .tv_nsec = UTIME_OMIT}, + {.tv_sec = 7, .tv_nsec = 8}, + }; + + EXPECT_LOOKUP(1, RELPATH) + .WillOnce(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_valid = UINT64_MAX; + out->body.entry.attr.atime = oldtimes[0].tv_sec; + out->body.entry.attr.atimensec = oldtimes[0].tv_nsec; + out->body.entry.attr.mtime = oldtimes[1].tv_sec; + out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec; + }))); + + /* + * Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR + * call + */ + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_GETATTR && + 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 | 0644; + out->body.attr.attr.atime = oldtimes[0].tv_sec; + out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; + out->body.attr.attr.mtime = oldtimes[1].tv_sec; + out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec; + }))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + /* In protocol 7.23, ctime will be changed too */ + uint32_t valid = FATTR_MTIME; + return (in->header.opcode == FUSE_SETATTR && + in->header.nodeid == ino && + in->body.setattr.valid == valid && + in->body.setattr.mtime == newtimes[1].tv_sec && + in->body.setattr.mtimensec == + newtimes[1].tv_nsec); + }, 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 | 0644; + out->body.attr.attr.atime = oldtimes[0].tv_sec; + out->body.attr.attr.atimensec = oldtimes[0].tv_nsec; + out->body.attr.attr.mtime = newtimes[1].tv_sec; + out->body.attr.attr.mtimensec = newtimes[1].tv_nsec; + }))); + EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0)) + << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/setattr.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/statfs.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/statfs.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/statfs.cc (revision 345356) @@ -0,0 +1,118 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Statfs: public FuseTest {}; + +TEST_F(Statfs, eio) +{ + struct statfs statbuf; + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_STATFS); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(EIO))); + + ASSERT_NE(0, statfs("mountpoint", &statbuf)); + ASSERT_EQ(EIO, errno); +} + +/* + * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes + * the statfs(2) response, which is necessary for unmounting. + */ +TEST_F(Statfs, enotconn) +{ + struct statfs statbuf; + char mp[PATH_MAX]; + + m_mock->kill_daemon(); + + ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno); + strlcat(mp, "/mountpoint", PATH_MAX); + ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); + + EXPECT_EQ(getuid(), statbuf.f_owner); + EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); + EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); + EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); +} + +TEST_F(Statfs, ok) +{ + struct statfs statbuf; + char mp[PATH_MAX]; + + EXPECT_CALL(*m_mock, process( + ResultOf([](auto in) { + return (in->header.opcode == FUSE_STATFS); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, statfs); + out->body.statfs.st.blocks = 1000; + out->body.statfs.st.bfree = 100; + out->body.statfs.st.bavail = 200; + out->body.statfs.st.files = 5; + out->body.statfs.st.ffree = 6; + out->body.statfs.st.namelen = 128; + out->body.statfs.st.frsize = 1024; + }))); + + ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno); + strlcat(mp, "/mountpoint", PATH_MAX); + ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); + EXPECT_EQ(1024ul, statbuf.f_bsize); + /* + * fuse(4) ignores the filesystem's reported optimal transfer size, and + * chooses a size that works well with the rest of the system instead + */ + EXPECT_EQ(1000ul, statbuf.f_blocks); + EXPECT_EQ(100ul, statbuf.f_bfree); + EXPECT_EQ(200l, statbuf.f_bavail); + EXPECT_EQ(5ul, statbuf.f_files); + EXPECT_EQ(6l, statbuf.f_ffree); + EXPECT_EQ(128u, statbuf.f_namemax); + EXPECT_EQ(getuid(), statbuf.f_owner); + EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); + EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); + EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/statfs.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/fuse2/tests/sys/fs/fusefs/symlink.cc =================================================================== --- projects/fuse2/tests/sys/fs/fusefs/symlink.cc (nonexistent) +++ projects/fuse2/tests/sys/fs/fusefs/symlink.cc (revision 345356) @@ -0,0 +1,90 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * This software was developed by BFF Storage Systems, LLC under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern "C" { +#include +} + +#include "mockfs.hh" +#include "utils.hh" + +using namespace testing; + +class Symlink: public FuseTest {}; + +TEST_F(Symlink, enospc) +{ + const char FULLPATH[] = "mountpoint/lnk"; + const char RELPATH[] = "lnk"; + const char dst[] = "dst"; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes; + const char *linkname = name + strlen(name) + 1; + return (in->header.opcode == FUSE_SYMLINK && + (0 == strcmp(linkname, dst)) && + (0 == strcmp(name, RELPATH))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnErrno(ENOSPC))); + + EXPECT_EQ(-1, symlink(dst, FULLPATH)); + EXPECT_EQ(ENOSPC, errno); +} + +TEST_F(Symlink, ok) +{ + const char FULLPATH[] = "mountpoint/src"; + const char RELPATH[] = "src"; + const char dst[] = "dst"; + const uint64_t ino = 42; + + EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); + + EXPECT_CALL(*m_mock, process( + ResultOf([=](auto in) { + const char *name = (const char*)in->body.bytes; + const char *linkname = name + strlen(name) + 1; + return (in->header.opcode == FUSE_SYMLINK && + (0 == strcmp(linkname, dst)) && + (0 == strcmp(name, RELPATH))); + }, Eq(true)), + _) + ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { + SET_OUT_HEADER_LEN(out, entry); + out->body.entry.attr.mode = S_IFLNK | 0777; + out->body.entry.nodeid = ino; + }))); + + EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno); +} Property changes on: projects/fuse2/tests/sys/fs/fusefs/symlink.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property