]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/libcephfs/deleg.cc
update sources to v12.2.5
[ceph.git] / ceph / src / test / libcephfs / deleg.cc
CommitLineData
b32b8144
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Tests for Ceph delegation handling
5 *
6 * (c) 2017, Jeff Layton <jlayton@redhat.com>
7 */
8
9#include "gtest/gtest.h"
10#include "include/cephfs/libcephfs.h"
11#include "include/stat.h"
12#include <errno.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <dirent.h>
18#include <sys/xattr.h>
19#include <sys/uio.h>
20
21#ifdef __linux__
22#include <limits.h>
23#endif
24
25#include <map>
26#include <vector>
27#include <thread>
28#include <atomic>
29
94b18763
FG
30/* in ms -- 1 minute */
31#define MAX_WAIT (60 * 1000)
32
33static void wait_for_atomic_bool(std::atomic_bool &recalled)
34{
35 int i = 0;
36
37 while (!recalled.load()) {
38 ASSERT_LT(i++, MAX_WAIT);
39 usleep(1000);
40 }
41}
42
b32b8144
FG
43static int set_default_deleg_timeout(struct ceph_mount_info *cmount)
44{
45 uint32_t session_timeout = ceph_get_cap_return_timeout(cmount);
46 return ceph_set_deleg_timeout(cmount, session_timeout - 1);
47}
48
49static void dummy_deleg_cb(Fh *fh, void *priv)
50{
51 std::atomic_bool *recalled = (std::atomic_bool *)priv;
52 recalled->store(true);
53}
54
55static void open_breaker_func(struct ceph_mount_info *cmount, const char *filename, int flags, std::atomic_bool *opened)
56{
57 bool do_shutdown = false;
58
59 if (!cmount) {
60 ASSERT_EQ(ceph_create(&cmount, NULL), 0);
61 ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
62 ASSERT_EQ(ceph_conf_parse_env(cmount, NULL), 0);
63 ASSERT_EQ(ceph_mount(cmount, "/"), 0);
64 ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
65 do_shutdown = true;
66 }
67
68 Inode *root, *file;
69 ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
70
71 Fh *fh;
72 struct ceph_statx stx;
73 UserPerm *perms = ceph_mount_perms(cmount);
74
94b18763
FG
75 ASSERT_EQ(ceph_ll_lookup(cmount, root, filename, &file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
76 int ret, i = 0;
b32b8144 77 for (;;) {
94b18763 78 ASSERT_EQ(ceph_ll_getattr(cmount, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
b32b8144
FG
79 ret = ceph_ll_open(cmount, file, flags, &fh, perms);
80 if (ret != -EAGAIN)
81 break;
94b18763 82 ASSERT_LT(i++, MAX_WAIT);
b32b8144
FG
83 usleep(1000);
84 }
85 ASSERT_EQ(ret, 0);
86 opened->store(true);
87 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
88
89 if (do_shutdown)
90 ceph_shutdown(cmount);
91}
92
93enum {
94 DelegTestLink,
95 DelegTestRename,
96 DelegTestUnlink
97};
98
99static void namespace_breaker_func(struct ceph_mount_info *cmount, int cmd, const char *oldname, const char *newname)
100{
101 bool do_shutdown = false;
102
103 if (!cmount) {
104 ASSERT_EQ(ceph_create(&cmount, NULL), 0);
105 ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
106 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
107 ASSERT_EQ(ceph_mount(cmount, "/"), 0);
108 ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
109 do_shutdown = true;
110 }
111
112 Inode *root, *file = nullptr;
113 ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
114
115 struct ceph_statx stx;
116 UserPerm *perms = ceph_mount_perms(cmount);
117
94b18763 118 int ret, i = 0;
b32b8144
FG
119 for (;;) {
120 switch (cmd) {
121 case DelegTestRename:
122 ret = ceph_ll_rename(cmount, root, oldname, root, newname, perms);
123 break;
124 case DelegTestLink:
125 if (!file) {
126 ASSERT_EQ(ceph_ll_lookup(cmount, root, oldname, &file, &stx, 0, 0, perms), 0);
127 }
128 ret = ceph_ll_link(cmount, file, root, newname, perms);
129 break;
130 case DelegTestUnlink:
131 ret = ceph_ll_unlink(cmount, root, oldname, perms);
132 break;
133 default:
134 // Bad command
135 assert(false);
136 }
137 if (ret != -EAGAIN)
138 break;
94b18763 139 ASSERT_LT(i++, MAX_WAIT);
b32b8144
FG
140 usleep(1000);
141 }
142 ASSERT_EQ(ret, 0);
143
144 if (do_shutdown)
145 ceph_shutdown(cmount);
146}
147
148static void simple_deleg_test(struct ceph_mount_info *cmount, struct ceph_mount_info *tcmount)
149{
150 Inode *root, *file;
151
152 ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
153
154 char filename[32];
155
156 Fh *fh;
157 struct ceph_statx stx;
158 UserPerm *perms = ceph_mount_perms(cmount);
159
160 std::atomic_bool recalled(false);
161 std::atomic_bool opened(false);
162
163 // ensure r/w open breaks a r/w delegation
164 sprintf(filename, "deleg.rwrw.%x", getpid());
165 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
166 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
167 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
168 std::thread breaker1(open_breaker_func, tcmount, filename, O_RDWR, &opened);
94b18763
FG
169
170 wait_for_atomic_bool(recalled);
b32b8144
FG
171 ASSERT_EQ(opened.load(), false);
172 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
173 breaker1.join();
174 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
175 ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
176
177 // ensure r/o open breaks a r/w delegation
178 recalled.store(false);
179 opened.store(false);
180 sprintf(filename, "deleg.rorw.%x", getpid());
181 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
182 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
183 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
184 std::thread breaker2(open_breaker_func, tcmount, filename, O_RDONLY, &opened);
94b18763 185 wait_for_atomic_bool(recalled);
b32b8144
FG
186 ASSERT_EQ(opened.load(), false);
187 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
188 breaker2.join();
189 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
190 ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
191
192 // ensure r/o open does not break a r/o delegation
193 sprintf(filename, "deleg.rwro.%x", getpid());
194 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
195 O_RDONLY|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
196 recalled.store(false);
197 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
198 std::thread breaker3(open_breaker_func, tcmount, filename, O_RDONLY, &opened);
199 breaker3.join();
200 ASSERT_EQ(recalled.load(), false);
201
202 // ensure that r/w open breaks r/o delegation
203 opened.store(false);
204 std::thread breaker4(open_breaker_func, tcmount, filename, O_WRONLY, &opened);
94b18763 205 wait_for_atomic_bool(recalled);
b32b8144
FG
206 usleep(1000);
207 ASSERT_EQ(opened.load(), false);
208 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
209 breaker4.join();
210 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
211 ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
212
213 // ensure hardlinking breaks a r/w delegation
214 recalled.store(false);
215 char newname[32];
216 sprintf(filename, "deleg.old.%x", getpid());
217 sprintf(newname, "deleg.new.%x", getpid());
218 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
219 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
220 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
221 std::thread breaker5(namespace_breaker_func, tcmount, DelegTestLink, filename, newname);
94b18763 222 wait_for_atomic_bool(recalled);
b32b8144
FG
223 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
224 breaker5.join();
225 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
226 ASSERT_EQ(ceph_ll_unlink(cmount, root, filename, perms), 0);
227 ASSERT_EQ(ceph_ll_unlink(cmount, root, newname, perms), 0);
228
229 // ensure renaming breaks a r/w delegation
230 recalled.store(false);
231 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
232 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
233 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
234 std::thread breaker6(namespace_breaker_func, tcmount, DelegTestRename, filename, newname);
94b18763 235 wait_for_atomic_bool(recalled);
b32b8144
FG
236 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
237 breaker6.join();
238 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
239 ASSERT_EQ(ceph_ll_unlink(cmount, root, newname, perms), 0);
240
241 // ensure unlinking breaks a r/w delegation
242 recalled.store(false);
243 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
244 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
245 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_WR, dummy_deleg_cb, &recalled), 0);
246 std::thread breaker7(namespace_breaker_func, tcmount, DelegTestUnlink, filename, nullptr);
94b18763 247 wait_for_atomic_bool(recalled);
b32b8144
FG
248 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, &recalled), 0);
249 breaker7.join();
250 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
251}
252
253TEST(LibCephFS, DelegMultiClient) {
254 struct ceph_mount_info *cmount;
255
256 ASSERT_EQ(ceph_create(&cmount, NULL), 0);
257 ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
258 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
259 ASSERT_EQ(ceph_mount(cmount, "/"), 0);
260 ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
261
262 simple_deleg_test(cmount, nullptr);
263
264 ceph_shutdown(cmount);
265}
266
267TEST(LibCephFS, DelegSingleClient) {
268 struct ceph_mount_info *cmount;
269
270 ASSERT_EQ(ceph_create(&cmount, NULL), 0);
271 ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
272 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
273 ASSERT_EQ(ceph_mount(cmount, "/"), 0);
274 ASSERT_EQ(set_default_deleg_timeout(cmount), 0);
275
276 simple_deleg_test(cmount, cmount);
277
278 ceph_shutdown(cmount);
279}
280
281TEST(LibCephFS, DelegTimeout) {
282 struct ceph_mount_info *cmount;
283 ASSERT_EQ(ceph_create(&cmount, NULL), 0);
284 ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
285 ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
286 ASSERT_EQ(ceph_mount(cmount, "/"), 0);
287 // tweak timeout to run quickly, since we don't plan to return it anyway
288 ASSERT_EQ(ceph_set_deleg_timeout(cmount, 2), 0);
289
290 Inode *root, *file;
291 ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
292
293 char filename[32];
294 sprintf(filename, "delegtimeo%x", getpid());
295
296 Fh *fh;
297 struct ceph_statx stx;
298 UserPerm *perms = ceph_mount_perms(cmount);
299
300 ASSERT_EQ(ceph_ll_create(cmount, root, filename, 0666,
301 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
302
303 /* Reopen read-only */
304 ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
305 ASSERT_EQ(ceph_ll_open(cmount, file, O_RDONLY, &fh, perms), 0);
306
307 std::atomic_bool recalled(false);
308 ASSERT_EQ(ceph_ll_delegation(cmount, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
309 std::atomic_bool opened(false);
310 std::thread breaker1(open_breaker_func, nullptr, filename, O_RDWR, &opened);
311 breaker1.join();
312 ASSERT_EQ(recalled.load(), true);
313 ASSERT_EQ(ceph_ll_getattr(cmount, root, &stx, 0, 0, perms), -ENOTCONN);
314 ceph_release(cmount);
315}
94b18763
FG
316
317TEST(LibCephFS, RecalledGetattr) {
318 struct ceph_mount_info *cmount1;
319 ASSERT_EQ(ceph_create(&cmount1, NULL), 0);
320 ASSERT_EQ(ceph_conf_read_file(cmount1, NULL), 0);
321 ASSERT_EQ(0, ceph_conf_parse_env(cmount1, NULL));
322 ASSERT_EQ(ceph_mount(cmount1, "/"), 0);
323 ASSERT_EQ(set_default_deleg_timeout(cmount1), 0);
324
325 Inode *root, *file;
326 ASSERT_EQ(ceph_ll_lookup_root(cmount1, &root), 0);
327
328 char filename[32];
329 sprintf(filename, "recalledgetattr%x", getpid());
330
331 Fh *fh;
332 struct ceph_statx stx;
333 UserPerm *perms = ceph_mount_perms(cmount1);
334
335 ASSERT_EQ(ceph_ll_create(cmount1, root, filename, 0666,
336 O_RDWR|O_CREAT|O_EXCL, &file, &fh, &stx, 0, 0, perms), 0);
337 ASSERT_EQ(ceph_ll_write(cmount1, fh, 0, sizeof(filename), filename), sizeof(filename));
338 ASSERT_EQ(ceph_ll_close(cmount1, fh), 0);
339
340 /* New mount for read delegation */
341 struct ceph_mount_info *cmount2;
342 ASSERT_EQ(ceph_create(&cmount2, NULL), 0);
343 ASSERT_EQ(ceph_conf_read_file(cmount2, NULL), 0);
344 ASSERT_EQ(0, ceph_conf_parse_env(cmount2, NULL));
345 ASSERT_EQ(ceph_mount(cmount2, "/"), 0);
346 ASSERT_EQ(set_default_deleg_timeout(cmount2), 0);
347
348 ASSERT_EQ(ceph_ll_lookup_root(cmount2, &root), 0);
349 perms = ceph_mount_perms(cmount2);
350 ASSERT_EQ(ceph_ll_lookup(cmount2, root, filename, &file, &stx, 0, 0, perms), 0);
351
352 ASSERT_EQ(ceph_ll_open(cmount2, file, O_WRONLY, &fh, perms), 0);
353 ASSERT_EQ(ceph_ll_write(cmount2, fh, 0, sizeof(filename), filename), sizeof(filename));
354 ASSERT_EQ(ceph_ll_close(cmount2, fh), 0);
355
356 ASSERT_EQ(ceph_ll_open(cmount2, file, O_RDONLY, &fh, perms), 0);
357
358 /* Break delegation */
359 std::atomic_bool recalled(false);
360 ASSERT_EQ(ceph_ll_delegation(cmount2, fh, CEPH_DELEGATION_RD, dummy_deleg_cb, &recalled), 0);
361 ASSERT_EQ(ceph_ll_read(cmount2, fh, 0, sizeof(filename), filename), sizeof(filename));
362 ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
363 std::atomic_bool opened(false);
364 std::thread breaker1(open_breaker_func, cmount1, filename, O_WRONLY, &opened);
365 int i = 0;
366 do {
367 ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
368 ASSERT_LT(i++, MAX_WAIT);
369 usleep(1000);
370 } while (!recalled.load());
371 ASSERT_EQ(opened.load(), false);
372 ASSERT_EQ(ceph_ll_getattr(cmount2, file, &stx, CEPH_STATX_ALL_STATS, 0, perms), 0);
373 ASSERT_EQ(ceph_ll_delegation(cmount2, fh, CEPH_DELEGATION_NONE, dummy_deleg_cb, nullptr), 0);
374 breaker1.join();
375 ASSERT_EQ(ceph_ll_close(cmount2, fh), 0);
376 ceph_unmount(cmount2);
377 ceph_release(cmount2);
378 ceph_unmount(cmount1);
379 ceph_release(cmount1);
380}