1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
17 #define dout_subsys ceph_subsys_rbd
19 #define dout_prefix *_dout << "librbd::image::OpenRequest: "
24 using util::create_context_callback
;
25 using util::create_rados_callback
;
28 OpenRequest
<I
>::OpenRequest(I
*image_ctx
, uint64_t flags
,
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;
36 if ((flags
& OPEN_FLAG_IGNORE_MIGRATING
) != 0) {
37 m_image_ctx
->ignore_migrating
= true;
42 void OpenRequest
<I
>::send() {
43 if (m_image_ctx
->old_format
) {
44 send_v1_detect_header();
46 send_v2_detect_header();
51 void OpenRequest
<I
>::send_v1_detect_header() {
52 librados::ObjectReadOperation op
;
53 op
.stat(NULL
, NULL
, NULL
);
55 using klass
= OpenRequest
<I
>;
56 librados::AioCompletion
*comp
=
57 create_rados_callback
<klass
, &klass::handle_v1_detect_header
>(this);
59 m_image_ctx
->md_ctx
.aio_operate(util::old_header_name(m_image_ctx
->name
),
60 comp
, &op
, &m_out_bl
);
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
;
70 if (*result
!= -ENOENT
) {
71 lderr(cct
) << "failed to stat image header: " << cpp_strerror(*result
)
74 send_close_image(*result
);
76 ldout(cct
, 1) << "RBD image format 1 is deprecated. "
77 << "Please copy this image to image format 2." << dendl
;
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);
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
;
94 librados::ObjectReadOperation op
;
95 op
.stat(NULL
, NULL
, NULL
);
97 using klass
= OpenRequest
<I
>;
98 librados::AioCompletion
*comp
=
99 create_rados_callback
<klass
, &klass::handle_v2_detect_header
>(this);
101 m_image_ctx
->md_ctx
.aio_operate(util::id_obj_name(m_image_ctx
->name
),
102 comp
, &op
, &m_out_bl
);
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
;
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
)
119 send_close_image(*result
);
121 m_image_ctx
->old_format
= false;
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
;
132 librados::ObjectReadOperation op
;
133 cls_client::get_id_start(&op
);
135 using klass
= OpenRequest
<I
>;
136 librados::AioCompletion
*comp
=
137 create_rados_callback
<klass
, &klass::handle_v2_get_id
>(this);
139 m_image_ctx
->md_ctx
.aio_operate(util::id_obj_name(m_image_ctx
->name
),
140 comp
, &op
, &m_out_bl
);
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
;
150 auto it
= m_out_bl
.cbegin();
151 *result
= cls_client::get_id_finish(&it
, &m_image_ctx
->id
);
154 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(*result
)
156 send_close_image(*result
);
158 send_v2_get_initial_metadata();
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
;
168 librados::ObjectReadOperation op
;
169 cls_client::dir_get_name_start(&op
, m_image_ctx
->id
);
171 using klass
= OpenRequest
<I
>;
172 librados::AioCompletion
*comp
= create_rados_callback
<
173 klass
, &klass::handle_v2_get_name
>(this);
175 m_image_ctx
->md_ctx
.aio_operate(RBD_DIRECTORY
, comp
, &op
, &m_out_bl
);
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
;
185 auto it
= m_out_bl
.cbegin();
186 *result
= cls_client::dir_get_name_finish(&it
, &m_image_ctx
->name
);
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();
198 send_v2_get_initial_metadata();
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
;
208 librados::ObjectReadOperation op
;
209 cls_client::trash_get_start(&op
, m_image_ctx
->id
);
211 using klass
= OpenRequest
<I
>;
212 librados::AioCompletion
*comp
= create_rados_callback
<
213 klass
, &klass::handle_v2_get_name_from_trash
>(this);
215 m_image_ctx
->md_ctx
.aio_operate(RBD_TRASH
, comp
, &op
, &m_out_bl
);
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
;
224 cls::rbd::TrashImageSpec trash_spec
;
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
;
231 if (*result
== -EOPNOTSUPP
) {
234 if (*result
== -ENOENT
) {
235 ldout(cct
, 5) << "failed to retrieve name for image id "
236 << m_image_ctx
->id
<< dendl
;
238 lderr(cct
) << "failed to retrieve name from trash: "
239 << cpp_strerror(*result
) << dendl
;
241 send_close_image(*result
);
243 send_v2_get_initial_metadata();
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
;
254 m_image_ctx
->old_format
= false;
255 m_image_ctx
->header_oid
= util::header_name(m_image_ctx
->id
);
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);
262 using klass
= OpenRequest
<I
>;
263 librados::AioCompletion
*comp
= create_rados_callback
<
264 klass
, &klass::handle_v2_get_initial_metadata
>(this);
266 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
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
;
276 auto it
= m_out_bl
.cbegin();
279 *result
= cls_client::get_size_finish(&it
, &size
, &m_image_ctx
->order
);
283 *result
= cls_client::get_object_prefix_finish(&it
,
284 &m_image_ctx
->object_prefix
);
288 uint64_t incompatible_features
;
289 *result
= cls_client::get_features_finish(&it
, &m_image_ctx
->features
,
290 &incompatible_features
);
294 lderr(cct
) << "failed to retrieve initial metadata: "
295 << cpp_strerror(*result
) << dendl
;
296 send_close_image(*result
);
300 if (m_image_ctx
->test_features(RBD_FEATURE_STRIPINGV2
)) {
301 send_v2_get_stripe_unit_count();
303 send_v2_get_create_timestamp();
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
;
314 librados::ObjectReadOperation op
;
315 cls_client::get_stripe_unit_count_start(&op
);
317 using klass
= OpenRequest
<I
>;
318 librados::AioCompletion
*comp
= create_rados_callback
<
319 klass
, &klass::handle_v2_get_stripe_unit_count
>(this);
321 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
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
;
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
);
337 if (*result
== -ENOEXEC
|| *result
== -EINVAL
) {
342 lderr(cct
) << "failed to read striping metadata: " << cpp_strerror(*result
)
344 send_close_image(*result
);
348 send_v2_get_create_timestamp();
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
;
357 librados::ObjectReadOperation op
;
358 cls_client::get_create_timestamp_start(&op
);
360 using klass
= OpenRequest
<I
>;
361 librados::AioCompletion
*comp
= create_rados_callback
<
362 klass
, &klass::handle_v2_get_create_timestamp
>(this);
364 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
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
;
375 auto it
= m_out_bl
.cbegin();
376 *result
= cls_client::get_create_timestamp_finish(&it
,
377 &m_image_ctx
->create_timestamp
);
379 if (*result
< 0 && *result
!= -EOPNOTSUPP
) {
380 lderr(cct
) << "failed to retrieve create_timestamp: "
381 << cpp_strerror(*result
)
383 send_close_image(*result
);
387 send_v2_get_access_modify_timestamp();
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
;
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
401 using klass
= OpenRequest
<I
>;
402 librados::AioCompletion
*comp
= create_rados_callback
<
403 klass
, &klass::handle_v2_get_access_modify_timestamp
>(this);
405 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
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
;
416 auto it
= m_out_bl
.cbegin();
417 *result
= cls_client::get_access_timestamp_finish(&it
,
418 &m_image_ctx
->access_timestamp
);
420 *result
= cls_client::get_modify_timestamp_finish(&it
,
421 &m_image_ctx
->modify_timestamp
);
423 if (*result
< 0 && *result
!= -EOPNOTSUPP
) {
424 lderr(cct
) << "failed to retrieve access/modify_timestamp: "
425 << cpp_strerror(*result
)
427 send_close_image(*result
);
431 send_v2_get_data_pool();
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
;
440 librados::ObjectReadOperation op
;
441 cls_client::get_data_pool_start(&op
);
443 using klass
= OpenRequest
<I
>;
444 librados::AioCompletion
*comp
= create_rados_callback
<
445 klass
, &klass::handle_v2_get_data_pool
>(this);
447 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
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
;
457 int64_t data_pool_id
= -1;
459 auto it
= m_out_bl
.cbegin();
460 *result
= cls_client::get_data_pool_finish(&it
, &data_pool_id
);
461 } else if (*result
== -EOPNOTSUPP
) {
466 lderr(cct
) << "failed to read data pool: " << cpp_strerror(*result
)
468 send_close_image(*result
);
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
);
476 send_close_image(*result
);
479 m_image_ctx
->data_ctx
.set_namespace(m_image_ctx
->md_ctx
.get_namespace());
482 m_image_ctx
->init_layout();
487 template <typename I
>
488 void OpenRequest
<I
>::send_refresh() {
491 CephContext
*cct
= m_image_ctx
->cct
;
492 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
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));
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
;
507 lderr(cct
) << "failed to refresh image: " << cpp_strerror(*result
)
509 send_close_image(*result
);
513 return send_init_cache(result
);
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
);
523 CephContext
*cct
= m_image_ctx
->cct
;
524 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
526 auto cache
= cache::ObjectCacherObjectDispatch
<I
>::create(m_image_ctx
);
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"));
535 return send_register_watch(result
);
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
);
544 CephContext
*cct
= m_image_ctx
->cct
;
545 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
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
);
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
;
559 if (*result
== -EPERM
) {
560 ldout(cct
, 5) << "user does not have write permission" << dendl
;
561 send_close_image(*result
);
563 } else if (*result
< 0) {
564 lderr(cct
) << "failed to register watch: " << cpp_strerror(*result
)
566 send_close_image(*result
);
570 return send_set_snap(result
);
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
) {
581 CephContext
*cct
= m_image_ctx
->cct
;
582 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
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
);
591 if (snap_id
== CEPH_NOSNAP
) {
592 lderr(cct
) << "failed to find snapshot " << m_image_ctx
->snap_name
<< dendl
;
593 send_close_image(-ENOENT
);
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));
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
;
611 lderr(cct
) << "failed to set image snapshot: " << cpp_strerror(*result
)
613 send_close_image(*result
);
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
;
625 m_error_result
= error_result
;
627 using klass
= OpenRequest
<I
>;
628 Context
*ctx
= create_context_callback
<klass
, &klass::handle_close_image
>(
630 CloseRequest
<I
> *req
= CloseRequest
<I
>::create(m_image_ctx
, ctx
);
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
;
640 lderr(cct
) << "failed to close image: " << cpp_strerror(*result
) << dendl
;
642 if (m_error_result
< 0) {
643 *result
= m_error_result
;
649 } // namespace librbd
651 template class librbd::image::OpenRequest
<librbd::ImageCtx
>;