]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/link.cc
fusefs: make the tests more cplusplusy
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / link.cc
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation
5  *
6  * This software was developed by BFF Storage Systems, LLC under sponsorship
7  * from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 extern "C" {
32 #include <unistd.h>
33 }
34
35 #include "mockfs.hh"
36 #include "utils.hh"
37
38 using namespace testing;
39
40 class Link: public FuseTest {
41 public:
42 void expect_link(uint64_t ino, const char *relpath, mode_t mode, uint32_t nlink)
43 {
44         EXPECT_CALL(*m_mock, process(
45                 ResultOf([=](auto in) {
46                         const char *name = (const char*)in.body.bytes
47                                 + sizeof(struct fuse_link_in);
48                         return (in.header.opcode == FUSE_LINK &&
49                                 in.body.link.oldnodeid == ino &&
50                                 (0 == strcmp(name, relpath)));
51                 }, Eq(true)),
52                 _)
53         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
54                 SET_OUT_HEADER_LEN(out, entry);
55                 out.body.entry.nodeid = ino;
56                 out.body.entry.attr.mode = mode;
57                 out.body.entry.attr.nlink = nlink;
58                 out.body.entry.attr_valid = UINT64_MAX;
59                 out.body.entry.entry_valid = UINT64_MAX;
60         })));
61 }
62
63 void expect_lookup(const char *relpath, uint64_t ino)
64 {
65         FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1);
66 }
67 };
68
69 class Link_7_8: public FuseTest {
70 public:
71 virtual void SetUp() {
72         m_kernel_minor_version = 8;
73         FuseTest::SetUp();
74 }
75
76 void expect_link(uint64_t ino, const char *relpath, mode_t mode, uint32_t nlink)
77 {
78         EXPECT_CALL(*m_mock, process(
79                 ResultOf([=](auto in) {
80                         const char *name = (const char*)in.body.bytes
81                                 + sizeof(struct fuse_link_in);
82                         return (in.header.opcode == FUSE_LINK &&
83                                 in.body.link.oldnodeid == ino &&
84                                 (0 == strcmp(name, relpath)));
85                 }, Eq(true)),
86                 _)
87         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
88                 SET_OUT_HEADER_LEN(out, entry_7_8);
89                 out.body.entry.nodeid = ino;
90                 out.body.entry.attr.mode = mode;
91                 out.body.entry.attr.nlink = nlink;
92                 out.body.entry.attr_valid = UINT64_MAX;
93                 out.body.entry.entry_valid = UINT64_MAX;
94         })));
95 }
96
97 void expect_lookup(const char *relpath, uint64_t ino)
98 {
99         FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, 0, 1);
100 }
101 };
102
103 /*
104  * A successful link should clear the parent directory's attribute cache,
105  * because the fuse daemon should update its mtime and ctime
106  */
107 TEST_F(Link, clear_attr_cache)
108 {
109         const char FULLPATH[] = "mountpoint/src";
110         const char RELPATH[] = "src";
111         const char FULLDST[] = "mountpoint/dst";
112         const char RELDST[] = "dst";
113         const uint64_t ino = 42;
114         mode_t mode = S_IFREG | 0644;
115         struct stat sb;
116
117         EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
118         EXPECT_CALL(*m_mock, process(
119                 ResultOf([=](auto in) {
120                         return (in.header.opcode == FUSE_GETATTR &&
121                                 in.header.nodeid == 1);
122                 }, Eq(true)),
123                 _)
124         ).Times(2)
125         .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
126                 SET_OUT_HEADER_LEN(out, attr);
127                 out.body.attr.attr.ino = 1;
128                 out.body.attr.attr.mode = S_IFDIR | 0755;
129                 out.body.attr.attr_valid = UINT64_MAX;
130         })));
131
132         EXPECT_LOOKUP(1, RELDST)
133         .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
134                 SET_OUT_HEADER_LEN(out, entry);
135                 out.body.entry.attr.mode = mode;
136                 out.body.entry.nodeid = ino;
137                 out.body.entry.attr.nlink = 1;
138                 out.body.entry.attr_valid = UINT64_MAX;
139                 out.body.entry.entry_valid = UINT64_MAX;
140         })));
141         expect_link(ino, RELPATH, mode, 2);
142
143         EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
144         EXPECT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
145         EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno);
146 }
147
148 TEST_F(Link, emlink)
149 {
150         const char FULLPATH[] = "mountpoint/lnk";
151         const char RELPATH[] = "lnk";
152         const char FULLDST[] = "mountpoint/dst";
153         const char RELDST[] = "dst";
154         uint64_t dst_ino = 42;
155
156         EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
157         expect_lookup(RELDST, dst_ino);
158
159         EXPECT_CALL(*m_mock, process(
160                 ResultOf([=](auto in) {
161                         const char *name = (const char*)in.body.bytes
162                                 + sizeof(struct fuse_link_in);
163                         return (in.header.opcode == FUSE_LINK &&
164                                 in.body.link.oldnodeid == dst_ino &&
165                                 (0 == strcmp(name, RELPATH)));
166                 }, Eq(true)),
167                 _)
168         ).WillOnce(Invoke(ReturnErrno(EMLINK)));
169
170         EXPECT_EQ(-1, link(FULLDST, FULLPATH));
171         EXPECT_EQ(EMLINK, errno);
172 }
173
174 TEST_F(Link, ok)
175 {
176         const char FULLPATH[] = "mountpoint/src";
177         const char RELPATH[] = "src";
178         const char FULLDST[] = "mountpoint/dst";
179         const char RELDST[] = "dst";
180         const uint64_t ino = 42;
181         mode_t mode = S_IFREG | 0644;
182         struct stat sb;
183
184         EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
185         EXPECT_LOOKUP(1, RELDST)
186         .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
187                 SET_OUT_HEADER_LEN(out, entry);
188                 out.body.entry.attr.mode = mode;
189                 out.body.entry.nodeid = ino;
190                 out.body.entry.attr.nlink = 1;
191                 out.body.entry.attr_valid = UINT64_MAX;
192                 out.body.entry.entry_valid = UINT64_MAX;
193         })));
194         expect_link(ino, RELPATH, mode, 2);
195
196         ASSERT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
197         // Check that the original file's nlink count has increased.
198         ASSERT_EQ(0, stat(FULLDST, &sb)) << strerror(errno);
199         EXPECT_EQ(2ul, sb.st_nlink);
200 }
201
202 TEST_F(Link_7_8, ok)
203 {
204         const char FULLPATH[] = "mountpoint/src";
205         const char RELPATH[] = "src";
206         const char FULLDST[] = "mountpoint/dst";
207         const char RELDST[] = "dst";
208         const uint64_t ino = 42;
209         mode_t mode = S_IFREG | 0644;
210         struct stat sb;
211
212         EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
213         EXPECT_LOOKUP(1, RELDST)
214         .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
215                 SET_OUT_HEADER_LEN(out, entry_7_8);
216                 out.body.entry.attr.mode = mode;
217                 out.body.entry.nodeid = ino;
218                 out.body.entry.attr.nlink = 1;
219                 out.body.entry.attr_valid = UINT64_MAX;
220                 out.body.entry.entry_valid = UINT64_MAX;
221         })));
222         expect_link(ino, RELPATH, mode, 2);
223
224         ASSERT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
225         // Check that the original file's nlink count has increased.
226         ASSERT_EQ(0, stat(FULLDST, &sb)) << strerror(errno);
227         EXPECT_EQ(2ul, sb.st_nlink);
228 }