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" {
32*8399d764SAlan Somers #include <semaphore.h>
339821f1d3SAlan Somers #include <unistd.h>
349821f1d3SAlan Somers }
359821f1d3SAlan Somers
369821f1d3SAlan Somers #include "mockfs.hh"
379821f1d3SAlan Somers #include "utils.hh"
389821f1d3SAlan Somers
399821f1d3SAlan Somers using namespace testing;
409821f1d3SAlan Somers
41002e54b0SAlan Somers class Symlink: public FuseTest {
42002e54b0SAlan Somers public:
43002e54b0SAlan Somers
expect_symlink(uint64_t ino,const char * target,const char * relpath)44002e54b0SAlan Somers void expect_symlink(uint64_t ino, const char *target, const char *relpath)
45002e54b0SAlan Somers {
46002e54b0SAlan Somers EXPECT_CALL(*m_mock, process(
47002e54b0SAlan Somers ResultOf([=](auto in) {
4829edc611SAlan Somers const char *name = (const char*)in.body.bytes;
49002e54b0SAlan Somers const char *linkname = name + strlen(name) + 1;
5029edc611SAlan Somers return (in.header.opcode == FUSE_SYMLINK &&
51002e54b0SAlan Somers (0 == strcmp(linkname, target)) &&
52002e54b0SAlan Somers (0 == strcmp(name, relpath)));
53002e54b0SAlan Somers }, Eq(true)),
54002e54b0SAlan Somers _)
5529edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
56002e54b0SAlan Somers SET_OUT_HEADER_LEN(out, entry);
5729edc611SAlan Somers out.body.entry.attr.mode = S_IFLNK | 0777;
5829edc611SAlan Somers out.body.entry.nodeid = ino;
5929edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX;
6029edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX;
61002e54b0SAlan Somers })));
62002e54b0SAlan Somers }
63002e54b0SAlan Somers
64002e54b0SAlan Somers };
65002e54b0SAlan Somers
6616bd2d47SAlan Somers class Symlink_7_8: public FuseTest {
6716bd2d47SAlan Somers public:
SetUp()6816bd2d47SAlan Somers virtual void SetUp() {
6916bd2d47SAlan Somers m_kernel_minor_version = 8;
7016bd2d47SAlan Somers FuseTest::SetUp();
7116bd2d47SAlan Somers }
7216bd2d47SAlan Somers
expect_symlink(uint64_t ino,const char * target,const char * relpath)7316bd2d47SAlan Somers void expect_symlink(uint64_t ino, const char *target, const char *relpath)
7416bd2d47SAlan Somers {
7516bd2d47SAlan Somers EXPECT_CALL(*m_mock, process(
7616bd2d47SAlan Somers ResultOf([=](auto in) {
7729edc611SAlan Somers const char *name = (const char*)in.body.bytes;
7816bd2d47SAlan Somers const char *linkname = name + strlen(name) + 1;
7929edc611SAlan Somers return (in.header.opcode == FUSE_SYMLINK &&
8016bd2d47SAlan Somers (0 == strcmp(linkname, target)) &&
8116bd2d47SAlan Somers (0 == strcmp(name, relpath)));
8216bd2d47SAlan Somers }, Eq(true)),
8316bd2d47SAlan Somers _)
8429edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
8516bd2d47SAlan Somers SET_OUT_HEADER_LEN(out, entry_7_8);
8629edc611SAlan Somers out.body.entry.attr.mode = S_IFLNK | 0777;
8729edc611SAlan Somers out.body.entry.nodeid = ino;
8829edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX;
8929edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX;
9016bd2d47SAlan Somers })));
9116bd2d47SAlan Somers }
9216bd2d47SAlan Somers
9316bd2d47SAlan Somers };
9416bd2d47SAlan Somers
95002e54b0SAlan Somers /*
96002e54b0SAlan Somers * A successful symlink should clear the parent directory's attribute cache,
97002e54b0SAlan Somers * because the fuse daemon should update its mtime and ctime
98002e54b0SAlan Somers */
TEST_F(Symlink,clear_attr_cache)99002e54b0SAlan Somers TEST_F(Symlink, clear_attr_cache)
100002e54b0SAlan Somers {
101002e54b0SAlan Somers const char FULLPATH[] = "mountpoint/src";
102002e54b0SAlan Somers const char RELPATH[] = "src";
103002e54b0SAlan Somers const char dst[] = "dst";
104002e54b0SAlan Somers const uint64_t ino = 42;
105002e54b0SAlan Somers struct stat sb;
106002e54b0SAlan Somers
107a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
108a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT)));
109002e54b0SAlan Somers EXPECT_CALL(*m_mock, process(
110002e54b0SAlan Somers ResultOf([=](auto in) {
11129edc611SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
112a34cdd26SAlan Somers in.header.nodeid == FUSE_ROOT_ID);
113002e54b0SAlan Somers }, Eq(true)),
114002e54b0SAlan Somers _)
115002e54b0SAlan Somers ).Times(2)
11629edc611SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
117002e54b0SAlan Somers SET_OUT_HEADER_LEN(out, attr);
118a34cdd26SAlan Somers out.body.attr.attr.ino = FUSE_ROOT_ID;
11929edc611SAlan Somers out.body.attr.attr.mode = S_IFDIR | 0755;
12029edc611SAlan Somers out.body.attr.attr_valid = UINT64_MAX;
121002e54b0SAlan Somers })));
122002e54b0SAlan Somers expect_symlink(ino, dst, RELPATH);
123002e54b0SAlan Somers
124002e54b0SAlan Somers EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
125002e54b0SAlan Somers EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
126002e54b0SAlan Somers EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
127002e54b0SAlan Somers }
1289821f1d3SAlan Somers
TEST_F(Symlink,enospc)1299821f1d3SAlan Somers TEST_F(Symlink, enospc)
1309821f1d3SAlan Somers {
1319821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/lnk";
1329821f1d3SAlan Somers const char RELPATH[] = "lnk";
1339821f1d3SAlan Somers const char dst[] = "dst";
1349821f1d3SAlan Somers
135a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
136a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT)));
1379821f1d3SAlan Somers
1389821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
1399821f1d3SAlan Somers ResultOf([=](auto in) {
14029edc611SAlan Somers const char *name = (const char*)in.body.bytes;
1419821f1d3SAlan Somers const char *linkname = name + strlen(name) + 1;
14229edc611SAlan Somers return (in.header.opcode == FUSE_SYMLINK &&
1439821f1d3SAlan Somers (0 == strcmp(linkname, dst)) &&
1449821f1d3SAlan Somers (0 == strcmp(name, RELPATH)));
1459821f1d3SAlan Somers }, Eq(true)),
1469821f1d3SAlan Somers _)
1479821f1d3SAlan Somers ).WillOnce(Invoke(ReturnErrno(ENOSPC)));
1489821f1d3SAlan Somers
1499821f1d3SAlan Somers EXPECT_EQ(-1, symlink(dst, FULLPATH));
1509821f1d3SAlan Somers EXPECT_EQ(ENOSPC, errno);
1519821f1d3SAlan Somers }
1529821f1d3SAlan Somers
TEST_F(Symlink,ok)1539821f1d3SAlan Somers TEST_F(Symlink, ok)
1549821f1d3SAlan Somers {
1559821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/src";
1569821f1d3SAlan Somers const char RELPATH[] = "src";
1579821f1d3SAlan Somers const char dst[] = "dst";
1589821f1d3SAlan Somers const uint64_t ino = 42;
1599821f1d3SAlan Somers
160a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
161a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT)));
162002e54b0SAlan Somers expect_symlink(ino, dst, RELPATH);
1639821f1d3SAlan Somers
1649821f1d3SAlan Somers EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
1659821f1d3SAlan Somers }
16616bd2d47SAlan Somers
1670bef4927SAlan Somers /*
1680bef4927SAlan Somers * Nothing bad should happen if the server returns the parent's inode number
1690bef4927SAlan Somers * for the newly created symlink. Regression test for bug 263662.
1700bef4927SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263662
1710bef4927SAlan Somers */
TEST_F(Symlink,parent_ino)1720bef4927SAlan Somers TEST_F(Symlink, parent_ino)
1730bef4927SAlan Somers {
1740bef4927SAlan Somers const char FULLPATH[] = "mountpoint/parent/src";
1750bef4927SAlan Somers const char PPATH[] = "parent";
1760bef4927SAlan Somers const char RELPATH[] = "src";
1770bef4927SAlan Somers const char dst[] = "dst";
178*8399d764SAlan Somers sem_t sem;
1790bef4927SAlan Somers const uint64_t ino = 42;
1800bef4927SAlan Somers
181*8399d764SAlan Somers ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
182*8399d764SAlan Somers
1830bef4927SAlan Somers expect_lookup(PPATH, ino, S_IFDIR | 0755, 0, 1);
1840bef4927SAlan Somers EXPECT_LOOKUP(ino, RELPATH)
1850bef4927SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT)));
1860bef4927SAlan Somers expect_symlink(ino, dst, RELPATH);
187*8399d764SAlan Somers expect_forget(ino, 1, &sem);
1880bef4927SAlan Somers
1890bef4927SAlan Somers EXPECT_EQ(-1, symlink(dst, FULLPATH));
1900bef4927SAlan Somers EXPECT_EQ(EIO, errno);
191*8399d764SAlan Somers
192*8399d764SAlan Somers sem_wait(&sem);
193*8399d764SAlan Somers sem_destroy(&sem);
1940bef4927SAlan Somers }
1950bef4927SAlan Somers
TEST_F(Symlink_7_8,ok)19616bd2d47SAlan Somers TEST_F(Symlink_7_8, ok)
19716bd2d47SAlan Somers {
19816bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/src";
19916bd2d47SAlan Somers const char RELPATH[] = "src";
20016bd2d47SAlan Somers const char dst[] = "dst";
20116bd2d47SAlan Somers const uint64_t ino = 42;
20216bd2d47SAlan Somers
203a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
204a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT)));
20516bd2d47SAlan Somers expect_symlink(ino, dst, RELPATH);
20616bd2d47SAlan Somers
20716bd2d47SAlan Somers EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
20816bd2d47SAlan Somers }
209