xref: /freebsd/tests/sys/fs/fusefs/readlink.cc (revision 662ec2f781521c36b76af748d74bb0a3c2e27a76)
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