2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
38 using namespace testing;
40 class Mkdir: public FuseTest {};
41 class Mkdir_7_8: public FuseTest {
43 virtual void SetUp() {
44 m_kernel_minor_version = 8;
50 * EMLINK is possible on filesystems that limit the number of hard links to a
51 * single file, like early versions of BtrFS
55 const char FULLPATH[] = "mountpoint/some_dir";
56 const char RELPATH[] = "some_dir";
59 EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
61 EXPECT_CALL(*m_mock, process(
62 ResultOf([=](auto in) {
63 const char *name = (const char*)in->body.bytes +
64 sizeof(fuse_mkdir_in);
65 return (in->header.opcode == FUSE_MKDIR &&
66 in->body.mkdir.mode == (S_IFDIR | mode) &&
67 (0 == strcmp(RELPATH, name)));
70 ).WillOnce(Invoke(ReturnErrno(EMLINK)));
72 ASSERT_NE(1, mkdir(FULLPATH, mode));
73 ASSERT_EQ(EMLINK, errno);
77 * Creating a new directory after FUSE_LOOKUP returned a negative cache entry
79 TEST_F(Mkdir, entry_cache_negative)
81 const char FULLPATH[] = "mountpoint/some_file.txt";
82 const char RELPATH[] = "some_file.txt";
86 * Set entry_valid = 0 because this test isn't concerned with whether
87 * or not we actually cache negative entries, only with whether we
88 * interpret negative cache responses correctly.
90 struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
92 /* mkdir will first do a LOOKUP, adding a negative cache entry */
93 EXPECT_LOOKUP(1, RELPATH).WillOnce(ReturnNegativeCache(&entry_valid));
95 EXPECT_CALL(*m_mock, process(
96 ResultOf([=](auto in) {
97 const char *name = (const char*)in->body.bytes +
99 return (in->header.opcode == FUSE_MKDIR &&
100 in->body.mkdir.mode == (S_IFDIR | mode) &&
101 (0 == strcmp(RELPATH, name)));
104 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
105 SET_OUT_HEADER_LEN(out, entry);
106 out->body.create.entry.attr.mode = S_IFDIR | mode;
107 out->body.create.entry.nodeid = ino;
108 out->body.create.entry.entry_valid = UINT64_MAX;
109 out->body.create.entry.attr_valid = UINT64_MAX;
112 ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
116 * Creating a new directory should purge any negative namecache entries
118 TEST_F(Mkdir, entry_cache_negative_purge)
120 const char FULLPATH[] = "mountpoint/some_file.txt";
121 const char RELPATH[] = "some_file.txt";
124 struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0};
126 /* mkdir will first do a LOOKUP, adding a negative cache entry */
127 EXPECT_LOOKUP(1, RELPATH).Times(1)
128 .WillOnce(Invoke(ReturnNegativeCache(&entry_valid)))
129 .RetiresOnSaturation();
131 /* Then the MKDIR should purge the negative cache entry */
132 EXPECT_CALL(*m_mock, process(
133 ResultOf([=](auto in) {
134 const char *name = (const char*)in->body.bytes +
135 sizeof(fuse_open_in);
136 return (in->header.opcode == FUSE_MKDIR &&
137 in->body.mkdir.mode == (S_IFDIR | mode) &&
138 (0 == strcmp(RELPATH, name)));
141 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
142 SET_OUT_HEADER_LEN(out, entry);
143 out->body.entry.attr.mode = S_IFDIR | mode;
144 out->body.entry.nodeid = ino;
145 out->body.entry.attr_valid = UINT64_MAX;
148 ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
150 /* Finally, a subsequent lookup should query the daemon */
151 expect_lookup(RELPATH, ino, S_IFDIR | mode, 0, 1);
153 ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
158 const char FULLPATH[] = "mountpoint/some_dir";
159 const char RELPATH[] = "some_dir";
163 EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
165 EXPECT_CALL(*m_mock, process(
166 ResultOf([=](auto in) {
167 const char *name = (const char*)in->body.bytes +
168 sizeof(fuse_mkdir_in);
169 return (in->header.opcode == FUSE_MKDIR &&
170 in->body.mkdir.mode == (S_IFDIR | mode) &&
171 (0 == strcmp(RELPATH, name)));
174 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
175 SET_OUT_HEADER_LEN(out, entry);
176 out->body.create.entry.attr.mode = S_IFDIR | mode;
177 out->body.create.entry.nodeid = ino;
178 out->body.create.entry.entry_valid = UINT64_MAX;
179 out->body.create.entry.attr_valid = UINT64_MAX;
182 ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
185 TEST_F(Mkdir_7_8, ok)
187 const char FULLPATH[] = "mountpoint/some_dir";
188 const char RELPATH[] = "some_dir";
192 EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
194 EXPECT_CALL(*m_mock, process(
195 ResultOf([=](auto in) {
196 const char *name = (const char*)in->body.bytes +
197 sizeof(fuse_mkdir_in);
198 return (in->header.opcode == FUSE_MKDIR &&
199 in->body.mkdir.mode == (S_IFDIR | mode) &&
200 (0 == strcmp(RELPATH, name)));
203 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
204 SET_OUT_HEADER_LEN(out, entry_7_8);
205 out->body.create.entry.attr.mode = S_IFDIR | mode;
206 out->body.create.entry.nodeid = ino;
207 out->body.create.entry.entry_valid = UINT64_MAX;
208 out->body.create.entry.attr_valid = UINT64_MAX;
211 ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);