]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/getattr.cc
fusefs: Upgrade FUSE protocol to version 7.9.
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / getattr.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 #include "mockfs.hh"
32 #include "utils.hh"
33
34 using namespace testing;
35
36 class Getattr : public FuseTest {
37 public:
38 void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
39         uint64_t size, int times, uint64_t attr_valid, uint32_t attr_valid_nsec)
40 {
41         EXPECT_LOOKUP(1, relpath)
42         .Times(times)
43         .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
44                 SET_OUT_HEADER_LEN(out, entry);
45                 out->body.entry.attr.mode = mode;
46                 out->body.entry.nodeid = ino;
47                 out->body.entry.attr.nlink = 1;
48                 out->body.entry.attr_valid = attr_valid;
49                 out->body.entry.attr_valid_nsec = attr_valid_nsec;
50                 out->body.entry.attr.size = size;
51                 out->body.entry.entry_valid = UINT64_MAX;
52         })));
53 }
54 };
55
56 class Getattr_7_8: public FuseTest {
57 public:
58 virtual void SetUp() {
59         m_kernel_minor_version = 8;
60         FuseTest::SetUp();
61 }
62 };
63
64 /*
65  * If getattr returns a non-zero cache timeout, then subsequent VOP_GETATTRs
66  * should use the cached attributes, rather than query the daemon
67  */
68 TEST_F(Getattr, attr_cache)
69 {
70         const char FULLPATH[] = "mountpoint/some_file.txt";
71         const char RELPATH[] = "some_file.txt";
72         const uint64_t ino = 42;
73         struct stat sb;
74
75         EXPECT_LOOKUP(1, RELPATH)
76         .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
77                 SET_OUT_HEADER_LEN(out, entry);
78                 out->body.entry.attr.mode = S_IFREG | 0644;
79                 out->body.entry.nodeid = ino;
80                 out->body.entry.entry_valid = UINT64_MAX;
81         })));
82         EXPECT_CALL(*m_mock, process(
83                 ResultOf([](auto in) {
84                         return (in->header.opcode == FUSE_GETATTR &&
85                                 in->header.nodeid == ino);
86                 }, Eq(true)),
87                 _)
88         ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) {
89                 SET_OUT_HEADER_LEN(out, attr);
90                 out->body.attr.attr_valid = UINT64_MAX;
91                 out->body.attr.attr.ino = ino;  // Must match nodeid
92                 out->body.attr.attr.mode = S_IFREG | 0644;
93         })));
94         EXPECT_EQ(0, stat(FULLPATH, &sb));
95         /* The second stat(2) should use cached attributes */
96         EXPECT_EQ(0, stat(FULLPATH, &sb));
97 }
98
99 /*
100  * If getattr returns a finite but non-zero cache timeout, then we should
101  * discard the cached attributes and requery the daemon after the timeout
102  * period passes.
103  */
104 TEST_F(Getattr, attr_cache_timeout)
105 {
106         const char FULLPATH[] = "mountpoint/some_file.txt";
107         const char RELPATH[] = "some_file.txt";
108         const uint64_t ino = 42;
109         struct stat sb;
110
111         expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1, 0, 0);
112         EXPECT_CALL(*m_mock, process(
113                 ResultOf([](auto in) {
114                         return (in->header.opcode == FUSE_GETATTR &&
115                                 in->header.nodeid == ino);
116                 }, Eq(true)),
117                 _)
118         ).Times(2)
119         .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
120                 SET_OUT_HEADER_LEN(out, attr);
121                 out->body.attr.attr_valid_nsec = NAP_NS / 2;
122                 out->body.attr.attr_valid = 0;
123                 out->body.attr.attr.ino = ino;  // Must match nodeid
124                 out->body.attr.attr.mode = S_IFREG | 0644;
125         })));
126
127         EXPECT_EQ(0, stat(FULLPATH, &sb));
128         nap();
129         /* Timeout has expired. stat(2) should requery the daemon */
130         EXPECT_EQ(0, stat(FULLPATH, &sb));
131 }
132
133 TEST_F(Getattr, enoent)
134 {
135         const char FULLPATH[] = "mountpoint/some_file.txt";
136         const char RELPATH[] = "some_file.txt";
137         struct stat sb;
138         const uint64_t ino = 42;
139
140         expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1, 0, 0);
141         EXPECT_CALL(*m_mock, process(
142                 ResultOf([](auto in) {
143                         return (in->header.opcode == FUSE_GETATTR &&
144                                 in->header.nodeid == ino);
145                 }, Eq(true)),
146                 _)
147         ).WillOnce(Invoke(ReturnErrno(ENOENT)));
148         EXPECT_NE(0, stat(FULLPATH, &sb));
149         EXPECT_EQ(ENOENT, errno);
150 }
151
152 TEST_F(Getattr, ok)
153 {
154         const char FULLPATH[] = "mountpoint/some_file.txt";
155         const char RELPATH[] = "some_file.txt";
156         const uint64_t ino = 42;
157         struct stat sb;
158
159         expect_lookup(RELPATH, ino, S_IFREG | 0644, 1, 1, 0, 0);
160         EXPECT_CALL(*m_mock, process(
161                 ResultOf([](auto in) {
162                         return (in->header.opcode == FUSE_GETATTR &&
163                                 in->header.nodeid == ino);
164                 }, Eq(true)),
165                 _)
166         ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) {
167                 SET_OUT_HEADER_LEN(out, attr);
168                 out->body.attr.attr.ino = ino;  // Must match nodeid
169                 out->body.attr.attr.mode = S_IFREG | 0644;
170                 out->body.attr.attr.size = 1;
171                 out->body.attr.attr.blocks = 2;
172                 out->body.attr.attr.atime = 3;
173                 out->body.attr.attr.mtime = 4;
174                 out->body.attr.attr.ctime = 5;
175                 out->body.attr.attr.atimensec = 6;
176                 out->body.attr.attr.mtimensec = 7;
177                 out->body.attr.attr.ctimensec = 8;
178                 out->body.attr.attr.nlink = 9;
179                 out->body.attr.attr.uid = 10;
180                 out->body.attr.attr.gid = 11;
181                 out->body.attr.attr.rdev = 12;
182         })));
183
184         ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
185         EXPECT_EQ(1, sb.st_size);
186         EXPECT_EQ(2, sb.st_blocks);
187         EXPECT_EQ(3, sb.st_atim.tv_sec);
188         EXPECT_EQ(6, sb.st_atim.tv_nsec);
189         EXPECT_EQ(4, sb.st_mtim.tv_sec);
190         EXPECT_EQ(7, sb.st_mtim.tv_nsec);
191         EXPECT_EQ(5, sb.st_ctim.tv_sec);
192         EXPECT_EQ(8, sb.st_ctim.tv_nsec);
193         EXPECT_EQ(9ull, sb.st_nlink);
194         EXPECT_EQ(10ul, sb.st_uid);
195         EXPECT_EQ(11ul, sb.st_gid);
196         EXPECT_EQ(12ul, sb.st_rdev);
197         EXPECT_EQ(ino, sb.st_ino);
198         EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
199
200         //st_birthtim and st_flags are not supported by protocol 7.8.  They're
201         //only supported as OS-specific extensions to OSX.
202         //EXPECT_EQ(, sb.st_birthtim);
203         //EXPECT_EQ(, sb.st_flags);
204         
205         //FUSE can't set st_blksize until protocol 7.9
206 }
207
208 TEST_F(Getattr_7_8, ok)
209 {
210         const char FULLPATH[] = "mountpoint/some_file.txt";
211         const char RELPATH[] = "some_file.txt";
212         const uint64_t ino = 42;
213         struct stat sb;
214
215         EXPECT_LOOKUP(1, RELPATH)
216         .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
217                 SET_OUT_HEADER_LEN(out, entry_7_8);
218                 out->body.entry.attr.mode = S_IFREG | 0644;
219                 out->body.entry.nodeid = ino;
220                 out->body.entry.attr.nlink = 1;
221                 out->body.entry.attr.size = 1;
222         })));
223         EXPECT_CALL(*m_mock, process(
224                 ResultOf([](auto in) {
225                         return (in->header.opcode == FUSE_GETATTR &&
226                                 in->header.nodeid == ino);
227                 }, Eq(true)),
228                 _)
229         ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) {
230                 SET_OUT_HEADER_LEN(out, attr_7_8);
231                 out->body.attr.attr.ino = ino;  // Must match nodeid
232                 out->body.attr.attr.mode = S_IFREG | 0644;
233                 out->body.attr.attr.size = 1;
234                 out->body.attr.attr.blocks = 2;
235                 out->body.attr.attr.atime = 3;
236                 out->body.attr.attr.mtime = 4;
237                 out->body.attr.attr.ctime = 5;
238                 out->body.attr.attr.atimensec = 6;
239                 out->body.attr.attr.mtimensec = 7;
240                 out->body.attr.attr.ctimensec = 8;
241                 out->body.attr.attr.nlink = 9;
242                 out->body.attr.attr.uid = 10;
243                 out->body.attr.attr.gid = 11;
244                 out->body.attr.attr.rdev = 12;
245         })));
246
247         ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
248         EXPECT_EQ(1, sb.st_size);
249         EXPECT_EQ(2, sb.st_blocks);
250         EXPECT_EQ(3, sb.st_atim.tv_sec);
251         EXPECT_EQ(6, sb.st_atim.tv_nsec);
252         EXPECT_EQ(4, sb.st_mtim.tv_sec);
253         EXPECT_EQ(7, sb.st_mtim.tv_nsec);
254         EXPECT_EQ(5, sb.st_ctim.tv_sec);
255         EXPECT_EQ(8, sb.st_ctim.tv_nsec);
256         EXPECT_EQ(9ull, sb.st_nlink);
257         EXPECT_EQ(10ul, sb.st_uid);
258         EXPECT_EQ(11ul, sb.st_gid);
259         EXPECT_EQ(12ul, sb.st_rdev);
260         EXPECT_EQ(ino, sb.st_ino);
261         EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
262
263         //st_birthtim and st_flags are not supported by protocol 7.8.  They're
264         //only supported as OS-specific extensions to OSX.
265 }