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