19821f1d3SAlan Somers /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
39821f1d3SAlan Somers *
49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers *
69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers * from the FreeBSD Foundation.
89821f1d3SAlan Somers *
99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers * are met:
129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers *
189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers * SUCH DAMAGE.
299821f1d3SAlan Somers */
309821f1d3SAlan Somers
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers
349821f1d3SAlan Somers #include <unistd.h>
359821f1d3SAlan Somers }
369821f1d3SAlan Somers
379821f1d3SAlan Somers #include "mockfs.hh"
389821f1d3SAlan Somers #include "utils.hh"
399821f1d3SAlan Somers
409821f1d3SAlan Somers using namespace testing;
419821f1d3SAlan Somers
429821f1d3SAlan Somers class Readlink: public FuseTest {
439821f1d3SAlan Somers public:
expect_lookup(const char * relpath,uint64_t ino)449821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino)
459821f1d3SAlan Somers {
469821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 0, 1);
479821f1d3SAlan Somers }
expect_readlink(uint64_t ino,ProcessMockerT r)489821f1d3SAlan Somers void expect_readlink(uint64_t ino, ProcessMockerT r)
499821f1d3SAlan Somers {
509821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
519821f1d3SAlan Somers ResultOf([=](auto in) {
5229edc611SAlan Somers return (in.header.opcode == FUSE_READLINK &&
5329edc611SAlan Somers in.header.nodeid == ino);
549821f1d3SAlan Somers }, Eq(true)),
559821f1d3SAlan Somers _)
569821f1d3SAlan Somers ).WillOnce(Invoke(r));
579821f1d3SAlan Somers }
589821f1d3SAlan Somers
599821f1d3SAlan Somers };
609821f1d3SAlan Somers
619821f1d3SAlan Somers class PushSymlinksIn: public Readlink {
SetUp()629821f1d3SAlan Somers virtual void SetUp() {
639821f1d3SAlan Somers m_push_symlinks_in = true;
649821f1d3SAlan Somers Readlink::SetUp();
659821f1d3SAlan Somers }
669821f1d3SAlan Somers };
679821f1d3SAlan Somers
TEST_F(Readlink,eloop)689821f1d3SAlan Somers TEST_F(Readlink, eloop)
699821f1d3SAlan Somers {
709821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/src";
719821f1d3SAlan Somers const char RELPATH[] = "src";
729821f1d3SAlan Somers const uint64_t ino = 42;
739821f1d3SAlan Somers char buf[80];
749821f1d3SAlan Somers
759821f1d3SAlan Somers expect_lookup(RELPATH, ino);
769821f1d3SAlan Somers expect_readlink(ino, ReturnErrno(ELOOP));
779821f1d3SAlan Somers
789821f1d3SAlan Somers EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
799821f1d3SAlan Somers EXPECT_EQ(ELOOP, errno);
809821f1d3SAlan Somers }
819821f1d3SAlan Somers
82*662ec2f7SAlan Somers /*
83*662ec2f7SAlan Somers * If a malicious or buggy server returns a NUL in the FUSE_READLINK result, it
84*662ec2f7SAlan Somers * should be handled gracefully.
85*662ec2f7SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=274268
86*662ec2f7SAlan Somers */
TEST_F(Readlink,embedded_nul)87*662ec2f7SAlan Somers TEST_F(Readlink, embedded_nul)
88*662ec2f7SAlan Somers {
89*662ec2f7SAlan Somers const char FULLPATH[] = "mountpoint/src";
90*662ec2f7SAlan Somers const char RELPATH[] = "src";
91*662ec2f7SAlan Somers const char dst[] = "dst\0stuff";
92*662ec2f7SAlan Somers char buf[80];
93*662ec2f7SAlan Somers const uint64_t ino = 42;
94*662ec2f7SAlan Somers
95*662ec2f7SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
96*662ec2f7SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
97*662ec2f7SAlan Somers SET_OUT_HEADER_LEN(out, entry);
98*662ec2f7SAlan Somers out.body.entry.attr.mode = S_IFLNK | 0777;
99*662ec2f7SAlan Somers out.body.entry.nodeid = ino;
100*662ec2f7SAlan Somers out.body.entry.attr_valid = UINT64_MAX;
101*662ec2f7SAlan Somers out.body.entry.entry_valid = UINT64_MAX;
102*662ec2f7SAlan Somers })));
103*662ec2f7SAlan Somers
104*662ec2f7SAlan Somers EXPECT_CALL(*m_mock, process(
105*662ec2f7SAlan Somers ResultOf([=](auto in) {
106*662ec2f7SAlan Somers return (in.header.opcode == FUSE_READLINK &&
107*662ec2f7SAlan Somers in.header.nodeid == ino);
108*662ec2f7SAlan Somers }, Eq(true)),
109*662ec2f7SAlan Somers _)
110*662ec2f7SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
111*662ec2f7SAlan Somers memcpy(out.body.str, dst, sizeof(dst));
112*662ec2f7SAlan Somers out.header.len = sizeof(out.header) + sizeof(dst) + 1;
113*662ec2f7SAlan Somers })));
114*662ec2f7SAlan Somers
115*662ec2f7SAlan Somers EXPECT_EQ(-1, readlink(FULLPATH, buf, sizeof(buf)));
116*662ec2f7SAlan Somers EXPECT_EQ(EIO, errno);
117*662ec2f7SAlan Somers EXPECT_EQ(-1, access(FULLPATH, R_OK));
118*662ec2f7SAlan Somers EXPECT_EQ(EIO, errno);
119*662ec2f7SAlan Somers }
120*662ec2f7SAlan Somers
TEST_F(Readlink,ok)1219821f1d3SAlan Somers TEST_F(Readlink, ok)
1229821f1d3SAlan Somers {
1239821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/src";
1249821f1d3SAlan Somers const char RELPATH[] = "src";
1259821f1d3SAlan Somers const char dst[] = "dst";
1269821f1d3SAlan Somers const uint64_t ino = 42;
1279821f1d3SAlan Somers char buf[80];
1289821f1d3SAlan Somers
1299821f1d3SAlan Somers expect_lookup(RELPATH, ino);
13029edc611SAlan Somers expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto& out) {
13129edc611SAlan Somers strlcpy(out.body.str, dst, sizeof(out.body.str));
13229edc611SAlan Somers out.header.len = sizeof(out.header) + strlen(dst) + 1;
1339821f1d3SAlan Somers }));
1349821f1d3SAlan Somers
13529edc611SAlan Somers EXPECT_EQ(static_cast<ssize_t>(strlen(dst)) + 1,
1369821f1d3SAlan Somers readlink(FULLPATH, buf, sizeof(buf)));
1379821f1d3SAlan Somers EXPECT_STREQ(dst, buf);
1389821f1d3SAlan Somers }
1399821f1d3SAlan Somers
TEST_F(PushSymlinksIn,readlink)1409821f1d3SAlan Somers TEST_F(PushSymlinksIn, readlink)
1419821f1d3SAlan Somers {
1429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/src";
1439821f1d3SAlan Somers const char RELPATH[] = "src";
1449821f1d3SAlan Somers const char dst[] = "/dst";
1459821f1d3SAlan Somers const uint64_t ino = 42;
1469821f1d3SAlan Somers char buf[MAXPATHLEN], wd[MAXPATHLEN], want[MAXPATHLEN];
1479821f1d3SAlan Somers int len;
1489821f1d3SAlan Somers
1499821f1d3SAlan Somers expect_lookup(RELPATH, ino);
15029edc611SAlan Somers expect_readlink(ino, ReturnImmediate([=](auto in __unused, auto& out) {
15129edc611SAlan Somers strlcpy(out.body.str, dst, sizeof(out.body.str));
15229edc611SAlan Somers out.header.len = sizeof(out.header) + strlen(dst) + 1;
1539821f1d3SAlan Somers }));
1549821f1d3SAlan Somers
1555a0b9a27SAlan Somers ASSERT_NE(nullptr, getcwd(wd, sizeof(wd))) << strerror(errno);
1569821f1d3SAlan Somers len = snprintf(want, sizeof(want), "%s/mountpoint%s", wd, dst);
1579821f1d3SAlan Somers ASSERT_LE(0, len) << strerror(errno);
1589821f1d3SAlan Somers
15929edc611SAlan Somers EXPECT_EQ(static_cast<ssize_t>(len) + 1,
16029edc611SAlan Somers readlink(FULLPATH, buf, sizeof(buf)));
1619821f1d3SAlan Somers EXPECT_STREQ(want, buf);
1629821f1d3SAlan Somers }
163