1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "common/dout.h"
5 #include "common/errno.h"
6 #include "librbd/internal.h"
7 #include "librbd/ImageState.h"
8 #include "librbd/Journal.h"
9 #include "librbd/ObjectMap.h"
10 #include "librbd/ExclusiveLock.h"
11 #include "librbd/MirroringWatcher.h"
12 #include "librbd/journal/DisabledPolicy.h"
13 #include "librbd/journal/RemoveRequest.h"
14 #include "librbd/image/RemoveRequest.h"
15 #include "librbd/operation/TrimRequest.h"
16 #include "librbd/mirror/DisableRequest.h"
18 #define dout_subsys ceph_subsys_rbd
20 #define dout_prefix *_dout << "librbd::image::RemoveRequest: " << this << " " \
26 using librados::IoCtx
;
27 using util::create_context_callback
;
28 using util::create_async_context_callback
;
29 using util::create_rados_callback
;
32 RemoveRequest
<I
>::RemoveRequest(IoCtx
&ioctx
, const std::string
&image_name
,
33 const std::string
&image_id
, bool force
,
34 bool from_trash_remove
,
35 ProgressContext
&prog_ctx
,
36 ContextWQ
*op_work_queue
, Context
*on_finish
)
37 : m_ioctx(ioctx
), m_image_name(image_name
), m_image_id(image_id
),
38 m_force(force
), m_from_trash_remove(from_trash_remove
),
39 m_prog_ctx(prog_ctx
), m_op_work_queue(op_work_queue
),
40 m_on_finish(on_finish
) {
41 m_cct
= reinterpret_cast<CephContext
*>(m_ioctx
.cct());
43 m_image_ctx
= I::create((m_image_id
.empty() ? m_image_name
: std::string()),
44 m_image_id
, nullptr, m_ioctx
, false);
48 void RemoveRequest
<I
>::send() {
49 ldout(m_cct
, 20) << dendl
;
55 void RemoveRequest
<I
>::open_image() {
56 ldout(m_cct
, 20) << dendl
;
58 using klass
= RemoveRequest
<I
>;
59 Context
*ctx
= create_context_callback
<klass
, &klass::handle_open_image
>(
62 m_image_ctx
->state
->open(true, ctx
);
66 void RemoveRequest
<I
>::handle_open_image(int r
) {
67 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
70 m_image_ctx
->destroy();
71 m_image_ctx
= nullptr;
74 lderr(m_cct
) << "error opening image: " << cpp_strerror(r
) << dendl
;
83 m_image_id
= m_image_ctx
->id
;
84 m_image_name
= m_image_ctx
->name
;
85 m_header_oid
= m_image_ctx
->header_oid
;
86 m_old_format
= m_image_ctx
->old_format
;
87 m_unknown_format
= false;
89 check_exclusive_lock();
93 void RemoveRequest
<I
>::check_exclusive_lock() {
94 ldout(m_cct
, 20) << dendl
;
96 if (m_image_ctx
->exclusive_lock
== nullptr) {
97 validate_image_removal();
99 acquire_exclusive_lock();
104 void RemoveRequest
<I
>::acquire_exclusive_lock() {
105 ldout(m_cct
, 20) << dendl
;
107 // do not attempt to open the journal when removing the image in case
109 if (m_image_ctx
->test_features(RBD_FEATURE_JOURNALING
)) {
110 RWLock::WLocker
snap_locker(m_image_ctx
->snap_lock
);
111 m_image_ctx
->set_journal_policy(new journal::DisabledPolicy());
114 using klass
= RemoveRequest
<I
>;
116 Context
*ctx
= create_context_callback
<
117 klass
, &klass::handle_exclusive_lock_force
>(this);
118 m_exclusive_lock
= m_image_ctx
->exclusive_lock
;
119 m_exclusive_lock
->shut_down(ctx
);
121 Context
*ctx
= create_context_callback
<
122 klass
, &klass::handle_exclusive_lock
>(this);
123 RWLock::WLocker
owner_lock(m_image_ctx
->owner_lock
);
124 m_image_ctx
->exclusive_lock
->try_acquire_lock(ctx
);
129 void RemoveRequest
<I
>::handle_exclusive_lock_force(int r
) {
130 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
132 delete m_exclusive_lock
;
133 m_exclusive_lock
= nullptr;
136 lderr(m_cct
) << "error shutting down exclusive lock: "
137 << cpp_strerror(r
) << dendl
;
142 assert(m_image_ctx
->exclusive_lock
== nullptr);
143 validate_image_removal();
147 void RemoveRequest
<I
>::handle_exclusive_lock(int r
) {
148 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
150 if (r
< 0 || !m_image_ctx
->exclusive_lock
->is_lock_owner()) {
151 lderr(m_cct
) << "cannot obtain exclusive lock - not removing" << dendl
;
152 send_close_image(-EBUSY
);
156 validate_image_removal();
160 void RemoveRequest
<I
>::validate_image_removal() {
161 ldout(m_cct
, 20) << dendl
;
167 void RemoveRequest
<I
>::check_image_snaps() {
168 ldout(m_cct
, 20) << dendl
;
170 if (m_image_ctx
->snaps
.size()) {
171 lderr(m_cct
) << "image has snapshots - not removing" << dendl
;
172 send_close_image(-ENOTEMPTY
);
176 list_image_watchers();
180 void RemoveRequest
<I
>::list_image_watchers() {
181 ldout(m_cct
, 20) << dendl
;
183 librados::ObjectReadOperation op
;
184 op
.list_watchers(&m_watchers
, &m_ret_val
);
186 using klass
= RemoveRequest
<I
>;
187 librados::AioCompletion
*rados_completion
=
188 create_rados_callback
<klass
, &klass::handle_list_image_watchers
>(this);
190 int r
= m_image_ctx
->md_ctx
.aio_operate(m_header_oid
, rados_completion
,
193 rados_completion
->release();
197 void RemoveRequest
<I
>::handle_list_image_watchers(int r
) {
198 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
200 if (r
== 0 && m_ret_val
< 0) {
204 lderr(m_cct
) << "error listing image watchers: " << cpp_strerror(r
)
214 void RemoveRequest
<I
>::get_mirror_image() {
215 ldout(m_cct
, 20) << dendl
;
216 if ((m_watchers
.empty()) ||
217 ((m_image_ctx
->features
& RBD_FEATURE_JOURNALING
) == 0)) {
218 check_image_watchers();
222 librados::ObjectReadOperation op
;
223 cls_client::mirror_image_get_start(&op
, m_image_id
);
225 using klass
= RemoveRequest
<I
>;
226 librados::AioCompletion
*comp
=
227 create_rados_callback
<klass
, &klass::handle_get_mirror_image
>(this);
229 int r
= m_image_ctx
->md_ctx
.aio_operate(RBD_MIRRORING
, comp
, &op
, &m_out_bl
);
235 void RemoveRequest
<I
>::handle_get_mirror_image(int r
) {
236 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
238 if (r
== -ENOENT
|| r
== -EOPNOTSUPP
) {
239 check_image_watchers();
242 ldout(m_cct
, 5) << "error retrieving mirror image: " << cpp_strerror(r
)
246 list_mirror_watchers();
250 void RemoveRequest
<I
>::list_mirror_watchers() {
251 ldout(m_cct
, 20) << dendl
;
253 librados::ObjectReadOperation op
;
254 op
.list_watchers(&m_mirror_watchers
, &m_ret_val
);
256 using klass
= RemoveRequest
<I
>;
257 librados::AioCompletion
*rados_completion
=
258 create_rados_callback
<klass
, &klass::handle_list_mirror_watchers
>(this);
260 int r
= m_image_ctx
->md_ctx
.aio_operate(RBD_MIRRORING
, rados_completion
,
263 rados_completion
->release();
267 void RemoveRequest
<I
>::handle_list_mirror_watchers(int r
) {
268 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
270 if (r
== 0 && m_ret_val
< 0) {
273 if (r
< 0 && r
!= -ENOENT
) {
274 ldout(m_cct
, 5) << "error listing mirror watchers: " << cpp_strerror(r
)
278 for (auto &watcher
: m_mirror_watchers
) {
279 m_watchers
.remove_if([watcher
] (obj_watch_t
&w
) {
280 return (strncmp(w
.addr
, watcher
.addr
, sizeof(w
.addr
)) == 0);
284 check_image_watchers();
288 void RemoveRequest
<I
>::check_image_watchers() {
289 if (m_watchers
.size() > 1) {
290 lderr(m_cct
) << "image has watchers - not removing" << dendl
;
291 send_close_image(-EBUSY
);
299 void RemoveRequest
<I
>::check_group() {
300 ldout(m_cct
, 20) << dendl
;
302 librados::ObjectReadOperation op
;
303 librbd::cls_client::image_get_group_start(&op
);
305 using klass
= RemoveRequest
<I
>;
306 librados::AioCompletion
*rados_completion
= create_rados_callback
<
307 klass
, &klass::handle_check_group
>(this);
309 int r
= m_image_ctx
->md_ctx
.aio_operate(m_header_oid
, rados_completion
, &op
,
312 rados_completion
->release();
316 void RemoveRequest
<I
>::handle_check_group(int r
) {
317 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
319 cls::rbd::GroupSpec s
;
321 bufferlist::iterator it
= m_out_bl
.begin();
322 r
= librbd::cls_client::image_get_group_finish(&it
, &s
);
324 if (r
< 0 && r
!= -EOPNOTSUPP
) {
325 lderr(m_cct
) << "error fetching group for image: "
326 << cpp_strerror(r
) << dendl
;
332 lderr(m_cct
) << "image is in a group - not removing" << dendl
;
333 send_close_image(-EMLINK
);
341 void RemoveRequest
<I
>::trim_image() {
342 ldout(m_cct
, 20) << dendl
;
344 using klass
= RemoveRequest
<I
>;
345 Context
*ctx
= create_async_context_callback(
346 *m_image_ctx
, create_context_callback
<
347 klass
, &klass::handle_trim_image
>(this));
349 RWLock::RLocker
owner_lock(m_image_ctx
->owner_lock
);
350 auto req
= librbd::operation::TrimRequest
<I
>::create(
351 *m_image_ctx
, ctx
, m_image_ctx
->size
, 0, m_prog_ctx
);
356 void RemoveRequest
<I
>::handle_trim_image(int r
) {
357 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
360 lderr(m_cct
) << "warning: failed to remove some object(s): "
361 << cpp_strerror(r
) << dendl
;
373 void RemoveRequest
<I
>::remove_child() {
374 ldout(m_cct
, 20) << dendl
;
376 m_image_ctx
->parent_lock
.get_read();
377 ParentInfo parent_info
= m_image_ctx
->parent_md
;
378 m_image_ctx
->parent_lock
.put_read();
380 librados::ObjectWriteOperation op
;
381 librbd::cls_client::remove_child(&op
, parent_info
.spec
, m_image_id
);
383 using klass
= RemoveRequest
<I
>;
384 librados::AioCompletion
*rados_completion
=
385 create_rados_callback
<klass
, &klass::handle_remove_child
>(this);
386 int r
= m_image_ctx
->md_ctx
.aio_operate(RBD_CHILDREN
, rados_completion
, &op
);
388 rados_completion
->release();
392 void RemoveRequest
<I
>::handle_remove_child(int r
) {
393 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
398 lderr(m_cct
) << "error removing child from children list: "
399 << cpp_strerror(r
) << dendl
;
405 send_disable_mirror();
409 void RemoveRequest
<I
>::send_disable_mirror() {
410 ldout(m_cct
, 20) << dendl
;
412 using klass
= RemoveRequest
<I
>;
413 Context
*ctx
= create_context_callback
<
414 klass
, &klass::handle_disable_mirror
>(this);
416 mirror::DisableRequest
<I
> *req
=
417 mirror::DisableRequest
<I
>::create(m_image_ctx
, m_force
, !m_force
, ctx
);
422 void RemoveRequest
<I
>::handle_disable_mirror(int r
) {
423 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
425 if (r
== -EOPNOTSUPP
) {
428 lderr(m_cct
) << "error disabling image mirroring: "
429 << cpp_strerror(r
) << dendl
;
436 void RemoveRequest
<I
>::send_close_image(int r
) {
437 ldout(m_cct
, 20) << dendl
;
440 using klass
= RemoveRequest
<I
>;
441 Context
*ctx
= create_context_callback
<
442 klass
, &klass::handle_send_close_image
>(this);
444 m_image_ctx
->state
->close(ctx
);
448 void RemoveRequest
<I
>::handle_send_close_image(int r
) {
449 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
452 lderr(m_cct
) << "error encountered while closing image: "
453 << cpp_strerror(r
) << dendl
;
456 m_image_ctx
->destroy();
457 m_image_ctx
= nullptr;
468 void RemoveRequest
<I
>::remove_header() {
469 ldout(m_cct
, 20) << dendl
;
471 using klass
= RemoveRequest
<I
>;
472 librados::AioCompletion
*rados_completion
=
473 create_rados_callback
<klass
, &klass::handle_remove_header
>(this);
474 int r
= m_ioctx
.aio_remove(m_header_oid
, rados_completion
);
476 rados_completion
->release();
480 void RemoveRequest
<I
>::handle_remove_header(int r
) {
481 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
483 if (r
< 0 && r
!= -ENOENT
) {
484 lderr(m_cct
) << "error removing header: " << cpp_strerror(r
) << dendl
;
492 void RemoveRequest
<I
>::remove_header_v2() {
493 ldout(m_cct
, 20) << dendl
;
495 if (m_header_oid
.empty()) {
496 m_header_oid
= util::header_name(m_image_id
);
499 using klass
= RemoveRequest
<I
>;
500 librados::AioCompletion
*rados_completion
=
501 create_rados_callback
<klass
, &klass::handle_remove_header_v2
>(this);
502 int r
= m_ioctx
.aio_remove(m_header_oid
, rados_completion
);
504 rados_completion
->release();
508 void RemoveRequest
<I
>::handle_remove_header_v2(int r
) {
509 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
511 if (r
< 0 && r
!= -ENOENT
) {
512 lderr(m_cct
) << "error removing header: " << cpp_strerror(r
) << dendl
;
517 send_journal_remove();
521 void RemoveRequest
<I
>::send_journal_remove() {
522 ldout(m_cct
, 20) << dendl
;
524 using klass
= RemoveRequest
<I
>;
525 Context
*ctx
= create_context_callback
<
526 klass
, &klass::handle_journal_remove
>(this);
528 journal::RemoveRequest
<I
> *req
= journal::RemoveRequest
<I
>::create(
529 m_ioctx
, m_image_id
, Journal
<>::IMAGE_CLIENT_ID
, m_op_work_queue
, ctx
);
534 void RemoveRequest
<I
>::handle_journal_remove(int r
) {
535 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
537 if (r
< 0 && r
!= -ENOENT
) {
538 lderr(m_cct
) << "failed to remove image journal: " << cpp_strerror(r
)
546 send_object_map_remove();
550 void RemoveRequest
<I
>::send_object_map_remove() {
551 ldout(m_cct
, 20) << dendl
;
553 using klass
= RemoveRequest
<I
>;
554 librados::AioCompletion
*rados_completion
=
555 create_rados_callback
<klass
, &klass::handle_object_map_remove
>(this);
557 int r
= ObjectMap
<>::aio_remove(m_ioctx
,
561 rados_completion
->release();
565 void RemoveRequest
<I
>::handle_object_map_remove(int r
) {
566 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
568 if (r
< 0 && r
!= -ENOENT
) {
569 lderr(m_cct
) << "failed to remove image journal: " << cpp_strerror(r
)
577 mirror_image_remove();
581 void RemoveRequest
<I
>::mirror_image_remove() {
582 ldout(m_cct
, 20) << dendl
;
584 librados::ObjectWriteOperation op
;
585 cls_client::mirror_image_remove(&op
, m_image_id
);
587 using klass
= RemoveRequest
<I
>;
588 librados::AioCompletion
*rados_completion
=
589 create_rados_callback
<klass
, &klass::handle_mirror_image_remove
>(this);
590 int r
= m_ioctx
.aio_operate(RBD_MIRRORING
, rados_completion
, &op
);
592 rados_completion
->release();
596 void RemoveRequest
<I
>::handle_mirror_image_remove(int r
) {
597 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
599 if (r
< 0 && r
!= -ENOENT
&& r
!= -EOPNOTSUPP
) {
600 lderr(m_cct
) << "failed to remove mirror image state: "
601 << cpp_strerror(r
) << dendl
;
606 if (m_from_trash_remove
) {
607 // both the id object and the directory entry have been removed in
608 // a previous call to trash_move.
617 void RemoveRequest
<I
>::remove_image() {
618 ldout(m_cct
, 20) << dendl
;
620 if (m_old_format
|| m_unknown_format
) {
628 void RemoveRequest
<I
>::remove_v1_image() {
629 ldout(m_cct
, 20) << dendl
;
631 Context
*ctx
= new FunctionContext([this] (int r
) {
632 r
= tmap_rm(m_ioctx
, m_image_name
);
633 handle_remove_v1_image(r
);
636 m_op_work_queue
->queue(ctx
, 0);
640 void RemoveRequest
<I
>::handle_remove_v1_image(int r
) {
641 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
643 m_old_format
= (r
== 0);
644 if (r
== 0 || (r
< 0 && !m_unknown_format
)) {
645 if (r
< 0 && r
!= -ENOENT
) {
646 lderr(m_cct
) << "error removing image from v1 directory: "
647 << cpp_strerror(r
) << dendl
;
650 m_on_finish
->complete(r
);
661 void RemoveRequest
<I
>::remove_v2_image() {
662 ldout(m_cct
, 20) << dendl
;
664 if (m_image_id
.empty()) {
667 } else if (m_image_name
.empty()) {
668 dir_get_image_name();
677 void RemoveRequest
<I
>::dir_get_image_id() {
678 ldout(m_cct
, 20) << dendl
;
680 librados::ObjectReadOperation op
;
681 librbd::cls_client::dir_get_id_start(&op
, m_image_name
);
683 using klass
= RemoveRequest
<I
>;
684 librados::AioCompletion
*rados_completion
=
685 create_rados_callback
<klass
, &klass::handle_dir_get_image_id
>(this);
687 int r
= m_ioctx
.aio_operate(RBD_DIRECTORY
, rados_completion
, &op
, &m_out_bl
);
689 rados_completion
->release();
693 void RemoveRequest
<I
>::handle_dir_get_image_id(int r
) {
694 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
696 if (r
< 0 && r
!= -ENOENT
) {
697 lderr(m_cct
) << "error fetching image id: " << cpp_strerror(r
)
704 bufferlist::iterator iter
= m_out_bl
.begin();
705 r
= librbd::cls_client::dir_get_id_finish(&iter
, &m_image_id
);
716 void RemoveRequest
<I
>::dir_get_image_name() {
717 ldout(m_cct
, 20) << dendl
;
719 librados::ObjectReadOperation op
;
720 librbd::cls_client::dir_get_name_start(&op
, m_image_id
);
722 using klass
= RemoveRequest
<I
>;
723 librados::AioCompletion
*rados_completion
=
724 create_rados_callback
<klass
, &klass::handle_dir_get_image_name
>(this);
726 int r
= m_ioctx
.aio_operate(RBD_DIRECTORY
, rados_completion
, &op
, &m_out_bl
);
728 rados_completion
->release();
732 void RemoveRequest
<I
>::handle_dir_get_image_name(int r
) {
733 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
735 if (r
< 0 && r
!= -ENOENT
) {
736 lderr(m_cct
) << "error fetching image name: " << cpp_strerror(r
)
743 bufferlist::iterator iter
= m_out_bl
.begin();
744 r
= librbd::cls_client::dir_get_name_finish(&iter
, &m_image_name
);
755 void RemoveRequest
<I
>::remove_id_object() {
756 ldout(m_cct
, 20) << dendl
;
758 using klass
= RemoveRequest
<I
>;
759 librados::AioCompletion
*rados_completion
=
760 create_rados_callback
<klass
, &klass::handle_remove_id_object
>(this);
761 int r
= m_ioctx
.aio_remove(util::id_obj_name(m_image_name
), rados_completion
);
763 rados_completion
->release();
767 void RemoveRequest
<I
>::handle_remove_id_object(int r
) {
768 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
770 if (r
< 0 && r
!= -ENOENT
) {
771 lderr(m_cct
) << "error removing id object: " << cpp_strerror(r
)
781 void RemoveRequest
<I
>::dir_remove_image() {
782 ldout(m_cct
, 20) << dendl
;
784 librados::ObjectWriteOperation op
;
785 librbd::cls_client::dir_remove_image(&op
, m_image_name
, m_image_id
);
787 using klass
= RemoveRequest
<I
>;
788 librados::AioCompletion
*rados_completion
=
789 create_rados_callback
<klass
, &klass::handle_dir_remove_image
>(this);
790 int r
= m_ioctx
.aio_operate(RBD_DIRECTORY
, rados_completion
, &op
);
792 rados_completion
->release();
796 void RemoveRequest
<I
>::handle_dir_remove_image(int r
) {
797 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
799 if (r
< 0 && r
!= -ENOENT
) {
800 lderr(m_cct
) << "error removing image from v2 directory: "
801 << cpp_strerror(r
) << dendl
;
808 void RemoveRequest
<I
>::finish(int r
) {
809 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
811 m_on_finish
->complete(r
);
816 } // namespace librbd
818 template class librbd::image::RemoveRequest
<librbd::ImageCtx
>;