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/cache/WriteAroundObjectDispatch.h"
12 #include "librbd/cache/ParentCacheObjectDispatch.cc"
13 #include "librbd/image/CloseRequest.h"
14 #include "librbd/image/RefreshRequest.h"
15 #include "librbd/image/SetSnapRequest.h"
16 #include "librbd/io/SimpleSchedulerObjectDispatch.h"
17 #include <boost/algorithm/string/predicate.hpp>
18 #include "include/ceph_assert.h"
20 #define dout_subsys ceph_subsys_rbd
22 #define dout_prefix *_dout << "librbd::image::OpenRequest: "
27 using util::create_context_callback
;
28 using util::create_rados_callback
;
31 OpenRequest
<I
>::OpenRequest(I
*image_ctx
, uint64_t flags
,
33 : m_image_ctx(image_ctx
),
34 m_skip_open_parent_image(flags
& OPEN_FLAG_SKIP_OPEN_PARENT
),
35 m_on_finish(on_finish
), m_error_result(0) {
36 if ((flags
& OPEN_FLAG_OLD_FORMAT
) != 0) {
37 m_image_ctx
->old_format
= true;
39 if ((flags
& OPEN_FLAG_IGNORE_MIGRATING
) != 0) {
40 m_image_ctx
->ignore_migrating
= true;
45 void OpenRequest
<I
>::send() {
46 if (m_image_ctx
->old_format
) {
47 send_v1_detect_header();
49 send_v2_detect_header();
54 void OpenRequest
<I
>::send_v1_detect_header() {
55 librados::ObjectReadOperation op
;
56 op
.stat(NULL
, NULL
, NULL
);
58 using klass
= OpenRequest
<I
>;
59 librados::AioCompletion
*comp
=
60 create_rados_callback
<klass
, &klass::handle_v1_detect_header
>(this);
62 m_image_ctx
->md_ctx
.aio_operate(util::old_header_name(m_image_ctx
->name
),
63 comp
, &op
, &m_out_bl
);
68 Context
*OpenRequest
<I
>::handle_v1_detect_header(int *result
) {
69 CephContext
*cct
= m_image_ctx
->cct
;
70 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
73 if (*result
!= -ENOENT
) {
74 lderr(cct
) << "failed to stat image header: " << cpp_strerror(*result
)
77 send_close_image(*result
);
79 ldout(cct
, 1) << "RBD image format 1 is deprecated. "
80 << "Please copy this image to image format 2." << dendl
;
82 m_image_ctx
->old_format
= true;
83 m_image_ctx
->header_oid
= util::old_header_name(m_image_ctx
->name
);
84 m_image_ctx
->apply_metadata({}, true);
92 void OpenRequest
<I
>::send_v2_detect_header() {
93 if (m_image_ctx
->id
.empty()) {
94 CephContext
*cct
= m_image_ctx
->cct
;
95 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
97 librados::ObjectReadOperation op
;
98 op
.stat(NULL
, NULL
, NULL
);
100 using klass
= OpenRequest
<I
>;
101 librados::AioCompletion
*comp
=
102 create_rados_callback
<klass
, &klass::handle_v2_detect_header
>(this);
104 m_image_ctx
->md_ctx
.aio_operate(util::id_obj_name(m_image_ctx
->name
),
105 comp
, &op
, &m_out_bl
);
112 template <typename I
>
113 Context
*OpenRequest
<I
>::handle_v2_detect_header(int *result
) {
114 CephContext
*cct
= m_image_ctx
->cct
;
115 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
117 if (*result
== -ENOENT
) {
118 send_v1_detect_header();
119 } else if (*result
< 0) {
120 lderr(cct
) << "failed to stat v2 image header: " << cpp_strerror(*result
)
122 send_close_image(*result
);
124 m_image_ctx
->old_format
= false;
130 template <typename I
>
131 void OpenRequest
<I
>::send_v2_get_id() {
132 CephContext
*cct
= m_image_ctx
->cct
;
133 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
135 librados::ObjectReadOperation op
;
136 cls_client::get_id_start(&op
);
138 using klass
= OpenRequest
<I
>;
139 librados::AioCompletion
*comp
=
140 create_rados_callback
<klass
, &klass::handle_v2_get_id
>(this);
142 m_image_ctx
->md_ctx
.aio_operate(util::id_obj_name(m_image_ctx
->name
),
143 comp
, &op
, &m_out_bl
);
147 template <typename I
>
148 Context
*OpenRequest
<I
>::handle_v2_get_id(int *result
) {
149 CephContext
*cct
= m_image_ctx
->cct
;
150 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
153 auto it
= m_out_bl
.cbegin();
154 *result
= cls_client::get_id_finish(&it
, &m_image_ctx
->id
);
157 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(*result
)
159 send_close_image(*result
);
161 send_v2_get_initial_metadata();
166 template <typename I
>
167 void OpenRequest
<I
>::send_v2_get_name() {
168 CephContext
*cct
= m_image_ctx
->cct
;
169 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
171 librados::ObjectReadOperation op
;
172 cls_client::dir_get_name_start(&op
, m_image_ctx
->id
);
174 using klass
= OpenRequest
<I
>;
175 librados::AioCompletion
*comp
= create_rados_callback
<
176 klass
, &klass::handle_v2_get_name
>(this);
178 m_image_ctx
->md_ctx
.aio_operate(RBD_DIRECTORY
, comp
, &op
, &m_out_bl
);
182 template <typename I
>
183 Context
*OpenRequest
<I
>::handle_v2_get_name(int *result
) {
184 CephContext
*cct
= m_image_ctx
->cct
;
185 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
188 auto it
= m_out_bl
.cbegin();
189 *result
= cls_client::dir_get_name_finish(&it
, &m_image_ctx
->name
);
191 if (*result
< 0 && *result
!= -ENOENT
) {
192 lderr(cct
) << "failed to retrieve name: "
193 << cpp_strerror(*result
) << dendl
;
194 send_close_image(*result
);
195 } else if (*result
== -ENOENT
) {
196 // image does not exist in directory, look in the trash bin
197 ldout(cct
, 10) << "image id " << m_image_ctx
->id
<< " does not exist in "
198 << "rbd directory, searching in rbd trash..." << dendl
;
199 send_v2_get_name_from_trash();
201 send_v2_get_initial_metadata();
206 template <typename I
>
207 void OpenRequest
<I
>::send_v2_get_name_from_trash() {
208 CephContext
*cct
= m_image_ctx
->cct
;
209 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
211 librados::ObjectReadOperation op
;
212 cls_client::trash_get_start(&op
, m_image_ctx
->id
);
214 using klass
= OpenRequest
<I
>;
215 librados::AioCompletion
*comp
= create_rados_callback
<
216 klass
, &klass::handle_v2_get_name_from_trash
>(this);
218 m_image_ctx
->md_ctx
.aio_operate(RBD_TRASH
, comp
, &op
, &m_out_bl
);
222 template <typename I
>
223 Context
*OpenRequest
<I
>::handle_v2_get_name_from_trash(int *result
) {
224 CephContext
*cct
= m_image_ctx
->cct
;
225 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
227 cls::rbd::TrashImageSpec trash_spec
;
229 auto it
= m_out_bl
.cbegin();
230 *result
= cls_client::trash_get_finish(&it
, &trash_spec
);
231 m_image_ctx
->name
= trash_spec
.name
;
234 if (*result
== -EOPNOTSUPP
) {
237 if (*result
== -ENOENT
) {
238 ldout(cct
, 5) << "failed to retrieve name for image id "
239 << m_image_ctx
->id
<< dendl
;
241 lderr(cct
) << "failed to retrieve name from trash: "
242 << cpp_strerror(*result
) << dendl
;
244 send_close_image(*result
);
246 send_v2_get_initial_metadata();
252 template <typename I
>
253 void OpenRequest
<I
>::send_v2_get_initial_metadata() {
254 CephContext
*cct
= m_image_ctx
->cct
;
255 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
257 m_image_ctx
->old_format
= false;
258 m_image_ctx
->header_oid
= util::header_name(m_image_ctx
->id
);
260 librados::ObjectReadOperation op
;
261 cls_client::get_size_start(&op
, CEPH_NOSNAP
);
262 cls_client::get_object_prefix_start(&op
);
263 cls_client::get_features_start(&op
, true);
265 using klass
= OpenRequest
<I
>;
266 librados::AioCompletion
*comp
= create_rados_callback
<
267 klass
, &klass::handle_v2_get_initial_metadata
>(this);
269 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
274 template <typename I
>
275 Context
*OpenRequest
<I
>::handle_v2_get_initial_metadata(int *result
) {
276 CephContext
*cct
= m_image_ctx
->cct
;
277 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
279 auto it
= m_out_bl
.cbegin();
282 *result
= cls_client::get_size_finish(&it
, &size
, &m_image_ctx
->order
);
286 *result
= cls_client::get_object_prefix_finish(&it
,
287 &m_image_ctx
->object_prefix
);
291 uint64_t incompatible_features
;
292 *result
= cls_client::get_features_finish(&it
, &m_image_ctx
->features
,
293 &incompatible_features
);
297 lderr(cct
) << "failed to retrieve initial metadata: "
298 << cpp_strerror(*result
) << dendl
;
299 send_close_image(*result
);
303 if (m_image_ctx
->test_features(RBD_FEATURE_STRIPINGV2
)) {
304 send_v2_get_stripe_unit_count();
306 send_v2_get_create_timestamp();
312 template <typename I
>
313 void OpenRequest
<I
>::send_v2_get_stripe_unit_count() {
314 CephContext
*cct
= m_image_ctx
->cct
;
315 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
317 librados::ObjectReadOperation op
;
318 cls_client::get_stripe_unit_count_start(&op
);
320 using klass
= OpenRequest
<I
>;
321 librados::AioCompletion
*comp
= create_rados_callback
<
322 klass
, &klass::handle_v2_get_stripe_unit_count
>(this);
324 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
329 template <typename I
>
330 Context
*OpenRequest
<I
>::handle_v2_get_stripe_unit_count(int *result
) {
331 CephContext
*cct
= m_image_ctx
->cct
;
332 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
335 auto it
= m_out_bl
.cbegin();
336 *result
= cls_client::get_stripe_unit_count_finish(
337 &it
, &m_image_ctx
->stripe_unit
, &m_image_ctx
->stripe_count
);
340 if (*result
== -ENOEXEC
|| *result
== -EINVAL
) {
345 lderr(cct
) << "failed to read striping metadata: " << cpp_strerror(*result
)
347 send_close_image(*result
);
351 send_v2_get_create_timestamp();
355 template <typename I
>
356 void OpenRequest
<I
>::send_v2_get_create_timestamp() {
357 CephContext
*cct
= m_image_ctx
->cct
;
358 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
360 librados::ObjectReadOperation op
;
361 cls_client::get_create_timestamp_start(&op
);
363 using klass
= OpenRequest
<I
>;
364 librados::AioCompletion
*comp
= create_rados_callback
<
365 klass
, &klass::handle_v2_get_create_timestamp
>(this);
367 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
372 template <typename I
>
373 Context
*OpenRequest
<I
>::handle_v2_get_create_timestamp(int *result
) {
374 CephContext
*cct
= m_image_ctx
->cct
;
375 ldout(cct
, 10) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
378 auto it
= m_out_bl
.cbegin();
379 *result
= cls_client::get_create_timestamp_finish(&it
,
380 &m_image_ctx
->create_timestamp
);
382 if (*result
< 0 && *result
!= -EOPNOTSUPP
) {
383 lderr(cct
) << "failed to retrieve create_timestamp: "
384 << cpp_strerror(*result
)
386 send_close_image(*result
);
390 send_v2_get_access_modify_timestamp();
394 template <typename I
>
395 void OpenRequest
<I
>::send_v2_get_access_modify_timestamp() {
396 CephContext
*cct
= m_image_ctx
->cct
;
397 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
399 librados::ObjectReadOperation op
;
400 cls_client::get_access_timestamp_start(&op
);
401 cls_client::get_modify_timestamp_start(&op
);
402 //TODO: merge w/ create timestamp query after luminous EOLed
404 using klass
= OpenRequest
<I
>;
405 librados::AioCompletion
*comp
= create_rados_callback
<
406 klass
, &klass::handle_v2_get_access_modify_timestamp
>(this);
408 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
413 template <typename I
>
414 Context
*OpenRequest
<I
>::handle_v2_get_access_modify_timestamp(int *result
) {
415 CephContext
*cct
= m_image_ctx
->cct
;
416 ldout(cct
, 10) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
419 auto it
= m_out_bl
.cbegin();
420 *result
= cls_client::get_access_timestamp_finish(&it
,
421 &m_image_ctx
->access_timestamp
);
423 *result
= cls_client::get_modify_timestamp_finish(&it
,
424 &m_image_ctx
->modify_timestamp
);
426 if (*result
< 0 && *result
!= -EOPNOTSUPP
) {
427 lderr(cct
) << "failed to retrieve access/modify_timestamp: "
428 << cpp_strerror(*result
)
430 send_close_image(*result
);
434 send_v2_get_data_pool();
438 template <typename I
>
439 void OpenRequest
<I
>::send_v2_get_data_pool() {
440 CephContext
*cct
= m_image_ctx
->cct
;
441 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
443 librados::ObjectReadOperation op
;
444 cls_client::get_data_pool_start(&op
);
446 using klass
= OpenRequest
<I
>;
447 librados::AioCompletion
*comp
= create_rados_callback
<
448 klass
, &klass::handle_v2_get_data_pool
>(this);
450 m_image_ctx
->md_ctx
.aio_operate(m_image_ctx
->header_oid
, comp
, &op
,
455 template <typename I
>
456 Context
*OpenRequest
<I
>::handle_v2_get_data_pool(int *result
) {
457 CephContext
*cct
= m_image_ctx
->cct
;
458 ldout(cct
, 10) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
460 int64_t data_pool_id
= -1;
462 auto it
= m_out_bl
.cbegin();
463 *result
= cls_client::get_data_pool_finish(&it
, &data_pool_id
);
464 } else if (*result
== -EOPNOTSUPP
) {
469 lderr(cct
) << "failed to read data pool: " << cpp_strerror(*result
)
471 send_close_image(*result
);
475 if (data_pool_id
!= -1) {
476 *result
= util::create_ioctx(m_image_ctx
->md_ctx
, "data pool", data_pool_id
,
477 {}, &m_image_ctx
->data_ctx
);
479 if (*result
!= -ENOENT
) {
480 send_close_image(*result
);
483 m_image_ctx
->data_ctx
.close();
485 m_image_ctx
->data_ctx
.set_namespace(m_image_ctx
->md_ctx
.get_namespace());
488 data_pool_id
= m_image_ctx
->md_ctx
.get_id();
491 m_image_ctx
->init_layout(data_pool_id
);
496 template <typename I
>
497 void OpenRequest
<I
>::send_refresh() {
500 CephContext
*cct
= m_image_ctx
->cct
;
501 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
503 using klass
= OpenRequest
<I
>;
504 RefreshRequest
<I
> *req
= RefreshRequest
<I
>::create(
505 *m_image_ctx
, false, m_skip_open_parent_image
,
506 create_context_callback
<klass
, &klass::handle_refresh
>(this));
510 template <typename I
>
511 Context
*OpenRequest
<I
>::handle_refresh(int *result
) {
512 CephContext
*cct
= m_image_ctx
->cct
;
513 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
516 lderr(cct
) << "failed to refresh image: " << cpp_strerror(*result
)
518 send_close_image(*result
);
522 return send_parent_cache(result
);
525 template <typename I
>
526 Context
* OpenRequest
<I
>::send_parent_cache(int *result
) {
527 CephContext
*cct
= m_image_ctx
->cct
;
528 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
530 bool parent_cache_enabled
= m_image_ctx
->config
.template get_val
<bool>(
531 "rbd_parent_cache_enabled");
533 if (m_image_ctx
->child
== nullptr || !parent_cache_enabled
) {
534 return send_init_cache(result
);
537 auto parent_cache
= cache::ParentCacheObjectDispatch
<I
>::create(m_image_ctx
);
538 using klass
= OpenRequest
<I
>;
539 Context
*ctx
= create_context_callback
<
540 klass
, &klass::handle_parent_cache
>(this);
542 parent_cache
->init(ctx
);
546 template <typename I
>
547 Context
* OpenRequest
<I
>::handle_parent_cache(int* result
) {
548 CephContext
*cct
= m_image_ctx
->cct
;
549 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
552 lderr(cct
) << "failed to parent cache " << dendl
;
553 send_close_image(*result
);
557 return send_init_cache(result
);
560 template <typename I
>
561 Context
*OpenRequest
<I
>::send_init_cache(int *result
) {
562 // cache is disabled or parent image context
563 if (!m_image_ctx
->cache
|| m_image_ctx
->child
!= nullptr) {
564 return send_register_watch(result
);
567 CephContext
*cct
= m_image_ctx
->cct
;
568 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
570 size_t max_dirty
= m_image_ctx
->config
.template get_val
<Option::size_t>(
571 "rbd_cache_max_dirty");
572 auto writethrough_until_flush
= m_image_ctx
->config
.template get_val
<bool>(
573 "rbd_cache_writethrough_until_flush");
574 auto cache_policy
= m_image_ctx
->config
.template get_val
<std::string
>(
576 if (cache_policy
== "writearound") {
577 auto cache
= cache::WriteAroundObjectDispatch
<I
>::create(
578 m_image_ctx
, max_dirty
, writethrough_until_flush
);
580 } else if (cache_policy
== "writethrough" || cache_policy
== "writeback") {
581 if (cache_policy
== "writethrough") {
585 auto cache
= cache::ObjectCacherObjectDispatch
<I
>::create(
586 m_image_ctx
, max_dirty
, writethrough_until_flush
);
589 // readahead requires the object cacher cache
590 m_image_ctx
->readahead
.set_trigger_requests(
591 m_image_ctx
->config
.template get_val
<uint64_t>("rbd_readahead_trigger_requests"));
592 m_image_ctx
->readahead
.set_max_readahead_size(
593 m_image_ctx
->config
.template get_val
<Option::size_t>("rbd_readahead_max_bytes"));
595 return send_register_watch(result
);
598 template <typename I
>
599 Context
*OpenRequest
<I
>::send_register_watch(int *result
) {
600 if ((m_image_ctx
->read_only_flags
& IMAGE_READ_ONLY_FLAG_USER
) != 0U) {
601 return send_set_snap(result
);
604 CephContext
*cct
= m_image_ctx
->cct
;
605 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
607 using klass
= OpenRequest
<I
>;
608 Context
*ctx
= create_context_callback
<
609 klass
, &klass::handle_register_watch
>(this);
610 m_image_ctx
->register_watch(ctx
);
614 template <typename I
>
615 Context
*OpenRequest
<I
>::handle_register_watch(int *result
) {
616 CephContext
*cct
= m_image_ctx
->cct
;
617 ldout(cct
, 10) << this << " " << __func__
<< ": r=" << *result
<< dendl
;
619 if (*result
== -EPERM
) {
620 ldout(cct
, 5) << "user does not have write permission" << dendl
;
621 send_close_image(*result
);
623 } else if (*result
< 0) {
624 lderr(cct
) << "failed to register watch: " << cpp_strerror(*result
)
626 send_close_image(*result
);
630 return send_set_snap(result
);
633 template <typename I
>
634 Context
*OpenRequest
<I
>::send_set_snap(int *result
) {
635 if (m_image_ctx
->snap_name
.empty() &&
636 m_image_ctx
->open_snap_id
== CEPH_NOSNAP
) {
638 return finalize(*result
);
641 CephContext
*cct
= m_image_ctx
->cct
;
642 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
644 uint64_t snap_id
= CEPH_NOSNAP
;
645 std::swap(m_image_ctx
->open_snap_id
, snap_id
);
646 if (snap_id
== CEPH_NOSNAP
) {
647 std::shared_lock image_locker
{m_image_ctx
->image_lock
};
648 snap_id
= m_image_ctx
->get_snap_id(m_image_ctx
->snap_namespace
,
649 m_image_ctx
->snap_name
);
651 if (snap_id
== CEPH_NOSNAP
) {
652 lderr(cct
) << "failed to find snapshot " << m_image_ctx
->snap_name
<< dendl
;
653 send_close_image(-ENOENT
);
657 using klass
= OpenRequest
<I
>;
658 SetSnapRequest
<I
> *req
= SetSnapRequest
<I
>::create(
659 *m_image_ctx
, snap_id
,
660 create_context_callback
<klass
, &klass::handle_set_snap
>(this));
665 template <typename I
>
666 Context
*OpenRequest
<I
>::handle_set_snap(int *result
) {
667 CephContext
*cct
= m_image_ctx
->cct
;
668 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
671 lderr(cct
) << "failed to set image snapshot: " << cpp_strerror(*result
)
673 send_close_image(*result
);
677 return finalize(*result
);
680 template <typename I
>
681 Context
*OpenRequest
<I
>::finalize(int r
) {
683 auto io_scheduler_cfg
=
684 m_image_ctx
->config
.template get_val
<std::string
>("rbd_io_scheduler");
686 if (io_scheduler_cfg
== "simple" && !m_image_ctx
->read_only
) {
688 io::SimpleSchedulerObjectDispatch
<I
>::create(m_image_ctx
);
689 io_scheduler
->init();
696 template <typename I
>
697 void OpenRequest
<I
>::send_close_image(int error_result
) {
698 CephContext
*cct
= m_image_ctx
->cct
;
699 ldout(cct
, 10) << this << " " << __func__
<< dendl
;
701 m_error_result
= error_result
;
703 using klass
= OpenRequest
<I
>;
704 Context
*ctx
= create_context_callback
<klass
, &klass::handle_close_image
>(
706 CloseRequest
<I
> *req
= CloseRequest
<I
>::create(m_image_ctx
, ctx
);
710 template <typename I
>
711 Context
*OpenRequest
<I
>::handle_close_image(int *result
) {
712 CephContext
*cct
= m_image_ctx
->cct
;
713 ldout(cct
, 10) << __func__
<< ": r=" << *result
<< dendl
;
716 lderr(cct
) << "failed to close image: " << cpp_strerror(*result
) << dendl
;
718 if (m_error_result
< 0) {
719 *result
= m_error_result
;
725 } // namespace librbd
727 template class librbd::image::OpenRequest
<librbd::ImageCtx
>;