]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/image/OpenRequest.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / librbd / image / OpenRequest.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "librbd/image/OpenRequest.h"
5 #include "common/dout.h"
6 #include "common/errno.h"
7 #include "cls/rbd/cls_rbd_client.h"
8 #include "librbd/ImageCtx.h"
9 #include "librbd/Utils.h"
10 #include "librbd/cache/ObjectCacherObjectDispatch.h"
11 #include "librbd/image/CloseRequest.h"
12 #include "librbd/image/RefreshRequest.h"
13 #include "librbd/image/SetSnapRequest.h"
14 #include <boost/algorithm/string/predicate.hpp>
15 #include "include/ceph_assert.h"
16
17 #define dout_subsys ceph_subsys_rbd
18 #undef dout_prefix
19 #define dout_prefix *_dout << "librbd::image::OpenRequest: "
20
21 namespace librbd {
22 namespace image {
23
24 using util::create_context_callback;
25 using util::create_rados_callback;
26
27 template <typename I>
28 OpenRequest<I>::OpenRequest(I *image_ctx, uint64_t flags,
29 Context *on_finish)
30 : m_image_ctx(image_ctx),
31 m_skip_open_parent_image(flags & OPEN_FLAG_SKIP_OPEN_PARENT),
32 m_on_finish(on_finish), m_error_result(0) {
33 if ((flags & OPEN_FLAG_OLD_FORMAT) != 0) {
34 m_image_ctx->old_format = true;
35 }
36 if ((flags & OPEN_FLAG_IGNORE_MIGRATING) != 0) {
37 m_image_ctx->ignore_migrating = true;
38 }
39 }
40
41 template <typename I>
42 void OpenRequest<I>::send() {
43 if (m_image_ctx->old_format) {
44 send_v1_detect_header();
45 } else {
46 send_v2_detect_header();
47 }
48 }
49
50 template <typename I>
51 void OpenRequest<I>::send_v1_detect_header() {
52 librados::ObjectReadOperation op;
53 op.stat(NULL, NULL, NULL);
54
55 using klass = OpenRequest<I>;
56 librados::AioCompletion *comp =
57 create_rados_callback<klass, &klass::handle_v1_detect_header>(this);
58 m_out_bl.clear();
59 m_image_ctx->md_ctx.aio_operate(util::old_header_name(m_image_ctx->name),
60 comp, &op, &m_out_bl);
61 comp->release();
62 }
63
64 template <typename I>
65 Context *OpenRequest<I>::handle_v1_detect_header(int *result) {
66 CephContext *cct = m_image_ctx->cct;
67 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
68
69 if (*result < 0) {
70 if (*result != -ENOENT) {
71 lderr(cct) << "failed to stat image header: " << cpp_strerror(*result)
72 << dendl;
73 }
74 send_close_image(*result);
75 } else {
76 ldout(cct, 1) << "RBD image format 1 is deprecated. "
77 << "Please copy this image to image format 2." << dendl;
78
79 m_image_ctx->old_format = true;
80 m_image_ctx->header_oid = util::old_header_name(m_image_ctx->name);
81 m_image_ctx->apply_metadata({}, true);
82
83 send_refresh();
84 }
85 return nullptr;
86 }
87
88 template <typename I>
89 void OpenRequest<I>::send_v2_detect_header() {
90 if (m_image_ctx->id.empty()) {
91 CephContext *cct = m_image_ctx->cct;
92 ldout(cct, 10) << this << " " << __func__ << dendl;
93
94 librados::ObjectReadOperation op;
95 op.stat(NULL, NULL, NULL);
96
97 using klass = OpenRequest<I>;
98 librados::AioCompletion *comp =
99 create_rados_callback<klass, &klass::handle_v2_detect_header>(this);
100 m_out_bl.clear();
101 m_image_ctx->md_ctx.aio_operate(util::id_obj_name(m_image_ctx->name),
102 comp, &op, &m_out_bl);
103 comp->release();
104 } else {
105 send_v2_get_name();
106 }
107 }
108
109 template <typename I>
110 Context *OpenRequest<I>::handle_v2_detect_header(int *result) {
111 CephContext *cct = m_image_ctx->cct;
112 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
113
114 if (*result == -ENOENT) {
115 send_v1_detect_header();
116 } else if (*result < 0) {
117 lderr(cct) << "failed to stat v2 image header: " << cpp_strerror(*result)
118 << dendl;
119 send_close_image(*result);
120 } else {
121 m_image_ctx->old_format = false;
122 send_v2_get_id();
123 }
124 return nullptr;
125 }
126
127 template <typename I>
128 void OpenRequest<I>::send_v2_get_id() {
129 CephContext *cct = m_image_ctx->cct;
130 ldout(cct, 10) << this << " " << __func__ << dendl;
131
132 librados::ObjectReadOperation op;
133 cls_client::get_id_start(&op);
134
135 using klass = OpenRequest<I>;
136 librados::AioCompletion *comp =
137 create_rados_callback<klass, &klass::handle_v2_get_id>(this);
138 m_out_bl.clear();
139 m_image_ctx->md_ctx.aio_operate(util::id_obj_name(m_image_ctx->name),
140 comp, &op, &m_out_bl);
141 comp->release();
142 }
143
144 template <typename I>
145 Context *OpenRequest<I>::handle_v2_get_id(int *result) {
146 CephContext *cct = m_image_ctx->cct;
147 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
148
149 if (*result == 0) {
150 auto it = m_out_bl.cbegin();
151 *result = cls_client::get_id_finish(&it, &m_image_ctx->id);
152 }
153 if (*result < 0) {
154 lderr(cct) << "failed to retrieve image id: " << cpp_strerror(*result)
155 << dendl;
156 send_close_image(*result);
157 } else {
158 send_v2_get_initial_metadata();
159 }
160 return nullptr;
161 }
162
163 template <typename I>
164 void OpenRequest<I>::send_v2_get_name() {
165 CephContext *cct = m_image_ctx->cct;
166 ldout(cct, 10) << this << " " << __func__ << dendl;
167
168 librados::ObjectReadOperation op;
169 cls_client::dir_get_name_start(&op, m_image_ctx->id);
170
171 using klass = OpenRequest<I>;
172 librados::AioCompletion *comp = create_rados_callback<
173 klass, &klass::handle_v2_get_name>(this);
174 m_out_bl.clear();
175 m_image_ctx->md_ctx.aio_operate(RBD_DIRECTORY, comp, &op, &m_out_bl);
176 comp->release();
177 }
178
179 template <typename I>
180 Context *OpenRequest<I>::handle_v2_get_name(int *result) {
181 CephContext *cct = m_image_ctx->cct;
182 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
183
184 if (*result == 0) {
185 auto it = m_out_bl.cbegin();
186 *result = cls_client::dir_get_name_finish(&it, &m_image_ctx->name);
187 }
188 if (*result < 0 && *result != -ENOENT) {
189 lderr(cct) << "failed to retrieve name: "
190 << cpp_strerror(*result) << dendl;
191 send_close_image(*result);
192 } else if (*result == -ENOENT) {
193 // image does not exist in directory, look in the trash bin
194 ldout(cct, 10) << "image id " << m_image_ctx->id << " does not exist in "
195 << "rbd directory, searching in rbd trash..." << dendl;
196 send_v2_get_name_from_trash();
197 } else {
198 send_v2_get_initial_metadata();
199 }
200 return nullptr;
201 }
202
203 template <typename I>
204 void OpenRequest<I>::send_v2_get_name_from_trash() {
205 CephContext *cct = m_image_ctx->cct;
206 ldout(cct, 10) << this << " " << __func__ << dendl;
207
208 librados::ObjectReadOperation op;
209 cls_client::trash_get_start(&op, m_image_ctx->id);
210
211 using klass = OpenRequest<I>;
212 librados::AioCompletion *comp = create_rados_callback<
213 klass, &klass::handle_v2_get_name_from_trash>(this);
214 m_out_bl.clear();
215 m_image_ctx->md_ctx.aio_operate(RBD_TRASH, comp, &op, &m_out_bl);
216 comp->release();
217 }
218
219 template <typename I>
220 Context *OpenRequest<I>::handle_v2_get_name_from_trash(int *result) {
221 CephContext *cct = m_image_ctx->cct;
222 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
223
224 cls::rbd::TrashImageSpec trash_spec;
225 if (*result == 0) {
226 auto it = m_out_bl.cbegin();
227 *result = cls_client::trash_get_finish(&it, &trash_spec);
228 m_image_ctx->name = trash_spec.name;
229 }
230 if (*result < 0) {
231 if (*result == -EOPNOTSUPP) {
232 *result = -ENOENT;
233 }
234 if (*result == -ENOENT) {
235 ldout(cct, 5) << "failed to retrieve name for image id "
236 << m_image_ctx->id << dendl;
237 } else {
238 lderr(cct) << "failed to retrieve name from trash: "
239 << cpp_strerror(*result) << dendl;
240 }
241 send_close_image(*result);
242 } else {
243 send_v2_get_initial_metadata();
244 }
245
246 return nullptr;
247 }
248
249 template <typename I>
250 void OpenRequest<I>::send_v2_get_initial_metadata() {
251 CephContext *cct = m_image_ctx->cct;
252 ldout(cct, 10) << this << " " << __func__ << dendl;
253
254 m_image_ctx->old_format = false;
255 m_image_ctx->header_oid = util::header_name(m_image_ctx->id);
256
257 librados::ObjectReadOperation op;
258 cls_client::get_size_start(&op, CEPH_NOSNAP);
259 cls_client::get_object_prefix_start(&op);
260 cls_client::get_features_start(&op, true);
261
262 using klass = OpenRequest<I>;
263 librados::AioCompletion *comp = create_rados_callback<
264 klass, &klass::handle_v2_get_initial_metadata>(this);
265 m_out_bl.clear();
266 m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
267 &m_out_bl);
268 comp->release();
269 }
270
271 template <typename I>
272 Context *OpenRequest<I>::handle_v2_get_initial_metadata(int *result) {
273 CephContext *cct = m_image_ctx->cct;
274 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
275
276 auto it = m_out_bl.cbegin();
277 if (*result >= 0) {
278 uint64_t size;
279 *result = cls_client::get_size_finish(&it, &size, &m_image_ctx->order);
280 }
281
282 if (*result >= 0) {
283 *result = cls_client::get_object_prefix_finish(&it,
284 &m_image_ctx->object_prefix);
285 }
286
287 if (*result >= 0) {
288 uint64_t incompatible_features;
289 *result = cls_client::get_features_finish(&it, &m_image_ctx->features,
290 &incompatible_features);
291 }
292
293 if (*result < 0) {
294 lderr(cct) << "failed to retrieve initial metadata: "
295 << cpp_strerror(*result) << dendl;
296 send_close_image(*result);
297 return nullptr;
298 }
299
300 if (m_image_ctx->test_features(RBD_FEATURE_STRIPINGV2)) {
301 send_v2_get_stripe_unit_count();
302 } else {
303 send_v2_get_create_timestamp();
304 }
305
306 return nullptr;
307 }
308
309 template <typename I>
310 void OpenRequest<I>::send_v2_get_stripe_unit_count() {
311 CephContext *cct = m_image_ctx->cct;
312 ldout(cct, 10) << this << " " << __func__ << dendl;
313
314 librados::ObjectReadOperation op;
315 cls_client::get_stripe_unit_count_start(&op);
316
317 using klass = OpenRequest<I>;
318 librados::AioCompletion *comp = create_rados_callback<
319 klass, &klass::handle_v2_get_stripe_unit_count>(this);
320 m_out_bl.clear();
321 m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
322 &m_out_bl);
323 comp->release();
324 }
325
326 template <typename I>
327 Context *OpenRequest<I>::handle_v2_get_stripe_unit_count(int *result) {
328 CephContext *cct = m_image_ctx->cct;
329 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
330
331 if (*result == 0) {
332 auto it = m_out_bl.cbegin();
333 *result = cls_client::get_stripe_unit_count_finish(
334 &it, &m_image_ctx->stripe_unit, &m_image_ctx->stripe_count);
335 }
336
337 if (*result == -ENOEXEC || *result == -EINVAL) {
338 *result = 0;
339 }
340
341 if (*result < 0) {
342 lderr(cct) << "failed to read striping metadata: " << cpp_strerror(*result)
343 << dendl;
344 send_close_image(*result);
345 return nullptr;
346 }
347
348 send_v2_get_create_timestamp();
349 return nullptr;
350 }
351
352 template <typename I>
353 void OpenRequest<I>::send_v2_get_create_timestamp() {
354 CephContext *cct = m_image_ctx->cct;
355 ldout(cct, 10) << this << " " << __func__ << dendl;
356
357 librados::ObjectReadOperation op;
358 cls_client::get_create_timestamp_start(&op);
359
360 using klass = OpenRequest<I>;
361 librados::AioCompletion *comp = create_rados_callback<
362 klass, &klass::handle_v2_get_create_timestamp>(this);
363 m_out_bl.clear();
364 m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
365 &m_out_bl);
366 comp->release();
367 }
368
369 template <typename I>
370 Context *OpenRequest<I>::handle_v2_get_create_timestamp(int *result) {
371 CephContext *cct = m_image_ctx->cct;
372 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
373
374 if (*result == 0) {
375 auto it = m_out_bl.cbegin();
376 *result = cls_client::get_create_timestamp_finish(&it,
377 &m_image_ctx->create_timestamp);
378 }
379 if (*result < 0 && *result != -EOPNOTSUPP) {
380 lderr(cct) << "failed to retrieve create_timestamp: "
381 << cpp_strerror(*result)
382 << dendl;
383 send_close_image(*result);
384 return nullptr;
385 }
386
387 send_v2_get_access_modify_timestamp();
388 return nullptr;
389 }
390
391 template <typename I>
392 void OpenRequest<I>::send_v2_get_access_modify_timestamp() {
393 CephContext *cct = m_image_ctx->cct;
394 ldout(cct, 10) << this << " " << __func__ << dendl;
395
396 librados::ObjectReadOperation op;
397 cls_client::get_access_timestamp_start(&op);
398 cls_client::get_modify_timestamp_start(&op);
399 //TODO: merge w/ create timestamp query after luminous EOLed
400
401 using klass = OpenRequest<I>;
402 librados::AioCompletion *comp = create_rados_callback<
403 klass, &klass::handle_v2_get_access_modify_timestamp>(this);
404 m_out_bl.clear();
405 m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
406 &m_out_bl);
407 comp->release();
408 }
409
410 template <typename I>
411 Context *OpenRequest<I>::handle_v2_get_access_modify_timestamp(int *result) {
412 CephContext *cct = m_image_ctx->cct;
413 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
414
415 if (*result == 0) {
416 auto it = m_out_bl.cbegin();
417 *result = cls_client::get_access_timestamp_finish(&it,
418 &m_image_ctx->access_timestamp);
419 if (*result == 0)
420 *result = cls_client::get_modify_timestamp_finish(&it,
421 &m_image_ctx->modify_timestamp);
422 }
423 if (*result < 0 && *result != -EOPNOTSUPP) {
424 lderr(cct) << "failed to retrieve access/modify_timestamp: "
425 << cpp_strerror(*result)
426 << dendl;
427 send_close_image(*result);
428 return nullptr;
429 }
430
431 send_v2_get_data_pool();
432 return nullptr;
433 }
434
435 template <typename I>
436 void OpenRequest<I>::send_v2_get_data_pool() {
437 CephContext *cct = m_image_ctx->cct;
438 ldout(cct, 10) << this << " " << __func__ << dendl;
439
440 librados::ObjectReadOperation op;
441 cls_client::get_data_pool_start(&op);
442
443 using klass = OpenRequest<I>;
444 librados::AioCompletion *comp = create_rados_callback<
445 klass, &klass::handle_v2_get_data_pool>(this);
446 m_out_bl.clear();
447 m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op,
448 &m_out_bl);
449 comp->release();
450 }
451
452 template <typename I>
453 Context *OpenRequest<I>::handle_v2_get_data_pool(int *result) {
454 CephContext *cct = m_image_ctx->cct;
455 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
456
457 int64_t data_pool_id = -1;
458 if (*result == 0) {
459 auto it = m_out_bl.cbegin();
460 *result = cls_client::get_data_pool_finish(&it, &data_pool_id);
461 } else if (*result == -EOPNOTSUPP) {
462 *result = 0;
463 }
464
465 if (*result < 0) {
466 lderr(cct) << "failed to read data pool: " << cpp_strerror(*result)
467 << dendl;
468 send_close_image(*result);
469 return nullptr;
470 }
471
472 if (data_pool_id != -1) {
473 *result = util::create_ioctx(m_image_ctx->md_ctx, "data pool", data_pool_id,
474 {}, &m_image_ctx->data_ctx);
475 if (*result < 0) {
476 send_close_image(*result);
477 return nullptr;
478 }
479 m_image_ctx->data_ctx.set_namespace(m_image_ctx->md_ctx.get_namespace());
480 }
481
482 m_image_ctx->init_layout();
483 send_refresh();
484 return nullptr;
485 }
486
487 template <typename I>
488 void OpenRequest<I>::send_refresh() {
489 m_image_ctx->init();
490
491 CephContext *cct = m_image_ctx->cct;
492 ldout(cct, 10) << this << " " << __func__ << dendl;
493
494 using klass = OpenRequest<I>;
495 RefreshRequest<I> *req = RefreshRequest<I>::create(
496 *m_image_ctx, false, m_skip_open_parent_image,
497 create_context_callback<klass, &klass::handle_refresh>(this));
498 req->send();
499 }
500
501 template <typename I>
502 Context *OpenRequest<I>::handle_refresh(int *result) {
503 CephContext *cct = m_image_ctx->cct;
504 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
505
506 if (*result < 0) {
507 lderr(cct) << "failed to refresh image: " << cpp_strerror(*result)
508 << dendl;
509 send_close_image(*result);
510 return nullptr;
511 }
512
513 return send_init_cache(result);
514 }
515
516 template <typename I>
517 Context *OpenRequest<I>::send_init_cache(int *result) {
518 // cache is disabled or parent image context
519 if (!m_image_ctx->cache || m_image_ctx->child != nullptr) {
520 return send_register_watch(result);
521 }
522
523 CephContext *cct = m_image_ctx->cct;
524 ldout(cct, 10) << this << " " << __func__ << dendl;
525
526 auto cache = cache::ObjectCacherObjectDispatch<I>::create(m_image_ctx);
527 cache->init();
528
529 // readahead requires the cache
530 m_image_ctx->readahead.set_trigger_requests(
531 m_image_ctx->config.template get_val<uint64_t>("rbd_readahead_trigger_requests"));
532 m_image_ctx->readahead.set_max_readahead_size(
533 m_image_ctx->config.template get_val<Option::size_t>("rbd_readahead_max_bytes"));
534
535 return send_register_watch(result);
536 }
537
538 template <typename I>
539 Context *OpenRequest<I>::send_register_watch(int *result) {
540 if (m_image_ctx->read_only) {
541 return send_set_snap(result);
542 }
543
544 CephContext *cct = m_image_ctx->cct;
545 ldout(cct, 10) << this << " " << __func__ << dendl;
546
547 using klass = OpenRequest<I>;
548 Context *ctx = create_context_callback<
549 klass, &klass::handle_register_watch>(this);
550 m_image_ctx->register_watch(ctx);
551 return nullptr;
552 }
553
554 template <typename I>
555 Context *OpenRequest<I>::handle_register_watch(int *result) {
556 CephContext *cct = m_image_ctx->cct;
557 ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl;
558
559 if (*result == -EPERM) {
560 ldout(cct, 5) << "user does not have write permission" << dendl;
561 send_close_image(*result);
562 return nullptr;
563 } else if (*result < 0) {
564 lderr(cct) << "failed to register watch: " << cpp_strerror(*result)
565 << dendl;
566 send_close_image(*result);
567 return nullptr;
568 }
569
570 return send_set_snap(result);
571 }
572
573 template <typename I>
574 Context *OpenRequest<I>::send_set_snap(int *result) {
575 if (m_image_ctx->snap_name.empty() &&
576 m_image_ctx->open_snap_id == CEPH_NOSNAP) {
577 *result = 0;
578 return m_on_finish;
579 }
580
581 CephContext *cct = m_image_ctx->cct;
582 ldout(cct, 10) << this << " " << __func__ << dendl;
583
584 uint64_t snap_id = CEPH_NOSNAP;
585 std::swap(m_image_ctx->open_snap_id, snap_id);
586 if (snap_id == CEPH_NOSNAP) {
587 RWLock::RLocker snap_locker(m_image_ctx->snap_lock);
588 snap_id = m_image_ctx->get_snap_id(m_image_ctx->snap_namespace,
589 m_image_ctx->snap_name);
590 }
591 if (snap_id == CEPH_NOSNAP) {
592 lderr(cct) << "failed to find snapshot " << m_image_ctx->snap_name << dendl;
593 send_close_image(-ENOENT);
594 return nullptr;
595 }
596
597 using klass = OpenRequest<I>;
598 SetSnapRequest<I> *req = SetSnapRequest<I>::create(
599 *m_image_ctx, snap_id,
600 create_context_callback<klass, &klass::handle_set_snap>(this));
601 req->send();
602 return nullptr;
603 }
604
605 template <typename I>
606 Context *OpenRequest<I>::handle_set_snap(int *result) {
607 CephContext *cct = m_image_ctx->cct;
608 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
609
610 if (*result < 0) {
611 lderr(cct) << "failed to set image snapshot: " << cpp_strerror(*result)
612 << dendl;
613 send_close_image(*result);
614 return nullptr;
615 }
616
617 return m_on_finish;
618 }
619
620 template <typename I>
621 void OpenRequest<I>::send_close_image(int error_result) {
622 CephContext *cct = m_image_ctx->cct;
623 ldout(cct, 10) << this << " " << __func__ << dendl;
624
625 m_error_result = error_result;
626
627 using klass = OpenRequest<I>;
628 Context *ctx = create_context_callback<klass, &klass::handle_close_image>(
629 this);
630 CloseRequest<I> *req = CloseRequest<I>::create(m_image_ctx, ctx);
631 req->send();
632 }
633
634 template <typename I>
635 Context *OpenRequest<I>::handle_close_image(int *result) {
636 CephContext *cct = m_image_ctx->cct;
637 ldout(cct, 10) << __func__ << ": r=" << *result << dendl;
638
639 if (*result < 0) {
640 lderr(cct) << "failed to close image: " << cpp_strerror(*result) << dendl;
641 }
642 if (m_error_result < 0) {
643 *result = m_error_result;
644 }
645 return m_on_finish;
646 }
647
648 } // namespace image
649 } // namespace librbd
650
651 template class librbd::image::OpenRequest<librbd::ImageCtx>;