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/RemoveRequest.h"
13 #include "librbd/image/RemoveRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "librbd/mirror/DisableRequest.h"
17 #define dout_subsys ceph_subsys_rbd
19 #define dout_prefix *_dout << "librbd::image::RemoveRequest: " << this << " " \
25 using librados::IoCtx;
26 using util::create_context_callback;
27 using util::create_async_context_callback;
28 using util::create_rados_callback;
31 RemoveRequest<I>::RemoveRequest(IoCtx &ioctx, const std::string &image_name,
32 const std::string &image_id, bool force,
33 bool from_trash_remove,
34 ProgressContext &prog_ctx,
35 ContextWQ *op_work_queue, Context *on_finish)
36 : m_ioctx(ioctx), m_image_name(image_name), m_image_id(image_id),
37 m_force(force), m_from_trash_remove(from_trash_remove),
38 m_prog_ctx(prog_ctx), m_op_work_queue(op_work_queue),
39 m_on_finish(on_finish) {
40 m_cct = reinterpret_cast<CephContext *>(m_ioctx.cct());
42 m_image_ctx = I::create((m_image_id.empty() ? m_image_name : std::string()),
43 m_image_id, nullptr, m_ioctx, false);
47 void RemoveRequest<I>::send() {
48 ldout(m_cct, 20) << dendl;
54 void RemoveRequest<I>::open_image() {
55 ldout(m_cct, 20) << dendl;
57 using klass = RemoveRequest<I>;
58 Context *ctx = create_context_callback<klass, &klass::handle_open_image>(
61 m_image_ctx->state->open(true, ctx);
65 void RemoveRequest<I>::handle_open_image(int r) {
66 ldout(m_cct, 20) << "r=" << r << dendl;
69 m_image_ctx->destroy();
70 m_image_ctx = nullptr;
73 lderr(m_cct) << "error opening image: " << cpp_strerror(r) << dendl;
82 m_image_id = m_image_ctx->id;
83 m_image_name = m_image_ctx->name;
84 m_header_oid = m_image_ctx->header_oid;
85 m_old_format = m_image_ctx->old_format;
86 m_unknown_format = false;
88 check_exclusive_lock();
92 void RemoveRequest<I>::check_exclusive_lock() {
93 ldout(m_cct, 20) << dendl;
95 if (m_image_ctx->exclusive_lock == nullptr) {
96 validate_image_removal();
98 acquire_exclusive_lock();
103 void RemoveRequest<I>::acquire_exclusive_lock() {
104 ldout(m_cct, 20) << dendl;
106 using klass = RemoveRequest<I>;
108 Context *ctx = create_context_callback<
109 klass, &klass::handle_exclusive_lock_force>(this);
110 m_exclusive_lock = m_image_ctx->exclusive_lock;
111 m_exclusive_lock->shut_down(ctx);
113 Context *ctx = create_context_callback<
114 klass, &klass::handle_exclusive_lock>(this);
115 RWLock::WLocker owner_lock(m_image_ctx->owner_lock);
116 m_image_ctx->exclusive_lock->try_acquire_lock(ctx);
121 void RemoveRequest<I>::handle_exclusive_lock_force(int r) {
122 ldout(m_cct, 20) << "r=" << r << dendl;
124 delete m_exclusive_lock;
125 m_exclusive_lock = nullptr;
128 lderr(m_cct) << "error shutting down exclusive lock: "
129 << cpp_strerror(r) << dendl;
134 assert(m_image_ctx->exclusive_lock == nullptr);
135 validate_image_removal();
139 void RemoveRequest<I>::handle_exclusive_lock(int r) {
140 ldout(m_cct, 20) << "r=" << r << dendl;
142 if (r < 0 || !m_image_ctx->exclusive_lock->is_lock_owner()) {
143 lderr(m_cct) << "cannot obtain exclusive lock - not removing" << dendl;
144 send_close_image(-EBUSY);
148 validate_image_removal();
152 void RemoveRequest<I>::validate_image_removal() {
153 ldout(m_cct, 20) << dendl;
159 void RemoveRequest<I>::check_image_snaps() {
160 ldout(m_cct, 20) << dendl;
162 if (m_image_ctx->snaps.size()) {
163 lderr(m_cct) << "image has snapshots - not removing" << dendl;
164 send_close_image(-ENOTEMPTY);
168 list_image_watchers();
172 void RemoveRequest<I>::list_image_watchers() {
173 ldout(m_cct, 20) << dendl;
175 librados::ObjectReadOperation op;
176 op.list_watchers(&m_watchers, &m_ret_val);
178 using klass = RemoveRequest<I>;
179 librados::AioCompletion *rados_completion =
180 create_rados_callback<klass, &klass::handle_list_image_watchers>(this);
182 int r = m_image_ctx->md_ctx.aio_operate(m_header_oid, rados_completion,
185 rados_completion->release();
189 void RemoveRequest<I>::handle_list_image_watchers(int r) {
190 ldout(m_cct, 20) << "r=" << r << dendl;
192 if (r == 0 && m_ret_val < 0) {
196 lderr(m_cct) << "error listing image watchers: " << cpp_strerror(r)
206 void RemoveRequest<I>::get_mirror_image() {
207 ldout(m_cct, 20) << dendl;
208 if ((m_watchers.empty()) ||
209 ((m_image_ctx->features & RBD_FEATURE_JOURNALING) == 0)) {
210 check_image_watchers();
214 librados::ObjectReadOperation op;
215 cls_client::mirror_image_get_start(&op, m_image_id);
217 using klass = RemoveRequest<I>;
218 librados::AioCompletion *comp =
219 create_rados_callback<klass, &klass::handle_get_mirror_image>(this);
221 int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, comp, &op, &m_out_bl);
227 void RemoveRequest<I>::handle_get_mirror_image(int r) {
228 ldout(m_cct, 20) << "r=" << r << dendl;
230 if (r == -ENOENT || r == -EOPNOTSUPP) {
231 check_image_watchers();
234 ldout(m_cct, 5) << "error retrieving mirror image: " << cpp_strerror(r)
238 list_mirror_watchers();
242 void RemoveRequest<I>::list_mirror_watchers() {
243 ldout(m_cct, 20) << dendl;
245 librados::ObjectReadOperation op;
246 op.list_watchers(&m_mirror_watchers, &m_ret_val);
248 using klass = RemoveRequest<I>;
249 librados::AioCompletion *rados_completion =
250 create_rados_callback<klass, &klass::handle_list_mirror_watchers>(this);
252 int r = m_image_ctx->md_ctx.aio_operate(RBD_MIRRORING, rados_completion,
255 rados_completion->release();
259 void RemoveRequest<I>::handle_list_mirror_watchers(int r) {
260 ldout(m_cct, 20) << "r=" << r << dendl;
262 if (r == 0 && m_ret_val < 0) {
265 if (r < 0 && r != -ENOENT) {
266 ldout(m_cct, 5) << "error listing mirror watchers: " << cpp_strerror(r)
270 for (auto &watcher : m_mirror_watchers) {
271 m_watchers.remove_if([watcher] (obj_watch_t &w) {
272 return (strncmp(w.addr, watcher.addr, sizeof(w.addr)) == 0);
276 check_image_watchers();
280 void RemoveRequest<I>::check_image_watchers() {
281 if (m_watchers.size() > 1) {
282 lderr(m_cct) << "image has watchers - not removing" << dendl;
283 send_close_image(-EBUSY);
291 void RemoveRequest<I>::check_group() {
292 ldout(m_cct, 20) << dendl;
294 librados::ObjectReadOperation op;
295 librbd::cls_client::image_get_group_start(&op);
297 using klass = RemoveRequest<I>;
298 librados::AioCompletion *rados_completion = create_rados_callback<
299 klass, &klass::handle_check_group>(this);
301 int r = m_image_ctx->md_ctx.aio_operate(m_header_oid, rados_completion, &op,
304 rados_completion->release();
308 void RemoveRequest<I>::handle_check_group(int r) {
309 ldout(m_cct, 20) << "r=" << r << dendl;
311 cls::rbd::GroupSpec s;
313 bufferlist::iterator it = m_out_bl.begin();
314 r = librbd::cls_client::image_get_group_finish(&it, &s);
316 if (r < 0 && r != -EOPNOTSUPP) {
317 lderr(m_cct) << "error fetching group for image: "
318 << cpp_strerror(r) << dendl;
324 lderr(m_cct) << "image is in a group - not removing" << dendl;
325 send_close_image(-EMLINK);
333 void RemoveRequest<I>::trim_image() {
334 ldout(m_cct, 20) << dendl;
336 using klass = RemoveRequest<I>;
337 Context *ctx = create_async_context_callback(
338 *m_image_ctx, create_context_callback<
339 klass, &klass::handle_trim_image>(this));
341 RWLock::RLocker owner_lock(m_image_ctx->owner_lock);
342 auto req = librbd::operation::TrimRequest<I>::create(
343 *m_image_ctx, ctx, m_image_ctx->size, 0, m_prog_ctx);
348 void RemoveRequest<I>::handle_trim_image(int r) {
349 ldout(m_cct, 20) << "r=" << r << dendl;
352 lderr(m_cct) << "warning: failed to remove some object(s): "
353 << cpp_strerror(r) << dendl;
365 void RemoveRequest<I>::remove_child() {
366 ldout(m_cct, 20) << dendl;
368 m_image_ctx->parent_lock.get_read();
369 ParentInfo parent_info = m_image_ctx->parent_md;
370 m_image_ctx->parent_lock.put_read();
372 librados::ObjectWriteOperation op;
373 librbd::cls_client::remove_child(&op, parent_info.spec, m_image_id);
375 using klass = RemoveRequest<I>;
376 librados::AioCompletion *rados_completion =
377 create_rados_callback<klass, &klass::handle_remove_child>(this);
378 int r = m_image_ctx->md_ctx.aio_operate(RBD_CHILDREN, rados_completion, &op);
380 rados_completion->release();
384 void RemoveRequest<I>::handle_remove_child(int r) {
385 ldout(m_cct, 20) << "r=" << r << dendl;
390 lderr(m_cct) << "error removing child from children list: "
391 << cpp_strerror(r) << dendl;
397 send_disable_mirror();
401 void RemoveRequest<I>::send_disable_mirror() {
402 ldout(m_cct, 20) << dendl;
404 using klass = RemoveRequest<I>;
405 Context *ctx = create_context_callback<
406 klass, &klass::handle_disable_mirror>(this);
408 mirror::DisableRequest<I> *req =
409 mirror::DisableRequest<I>::create(m_image_ctx, m_force, !m_force, ctx);
414 void RemoveRequest<I>::handle_disable_mirror(int r) {
415 ldout(m_cct, 20) << "r=" << r << dendl;
417 if (r == -EOPNOTSUPP) {
420 lderr(m_cct) << "error disabling image mirroring: "
421 << cpp_strerror(r) << dendl;
428 void RemoveRequest<I>::send_close_image(int r) {
429 ldout(m_cct, 20) << dendl;
432 using klass = RemoveRequest<I>;
433 Context *ctx = create_context_callback<
434 klass, &klass::handle_send_close_image>(this);
436 m_image_ctx->state->close(ctx);
440 void RemoveRequest<I>::handle_send_close_image(int r) {
441 ldout(m_cct, 20) << "r=" << r << dendl;
444 lderr(m_cct) << "error encountered while closing image: "
445 << cpp_strerror(r) << dendl;
448 m_image_ctx->destroy();
449 m_image_ctx = nullptr;
460 void RemoveRequest<I>::remove_header() {
461 ldout(m_cct, 20) << dendl;
463 using klass = RemoveRequest<I>;
464 librados::AioCompletion *rados_completion =
465 create_rados_callback<klass, &klass::handle_remove_header>(this);
466 int r = m_ioctx.aio_remove(m_header_oid, rados_completion);
468 rados_completion->release();
472 void RemoveRequest<I>::handle_remove_header(int r) {
473 ldout(m_cct, 20) << "r=" << r << dendl;
475 if (r < 0 && r != -ENOENT) {
476 lderr(m_cct) << "error removing header: " << cpp_strerror(r) << dendl;
484 void RemoveRequest<I>::remove_header_v2() {
485 ldout(m_cct, 20) << dendl;
487 if (m_header_oid.empty()) {
488 m_header_oid = util::header_name(m_image_id);
491 using klass = RemoveRequest<I>;
492 librados::AioCompletion *rados_completion =
493 create_rados_callback<klass, &klass::handle_remove_header_v2>(this);
494 int r = m_ioctx.aio_remove(m_header_oid, rados_completion);
496 rados_completion->release();
500 void RemoveRequest<I>::handle_remove_header_v2(int r) {
501 ldout(m_cct, 20) << "r=" << r << dendl;
503 if (r < 0 && r != -ENOENT) {
504 lderr(m_cct) << "error removing header: " << cpp_strerror(r) << dendl;
509 send_journal_remove();
513 void RemoveRequest<I>::send_journal_remove() {
514 ldout(m_cct, 20) << dendl;
516 using klass = RemoveRequest<I>;
517 Context *ctx = create_context_callback<
518 klass, &klass::handle_journal_remove>(this);
520 journal::RemoveRequest<I> *req = journal::RemoveRequest<I>::create(
521 m_ioctx, m_image_id, Journal<>::IMAGE_CLIENT_ID, m_op_work_queue, ctx);
526 void RemoveRequest<I>::handle_journal_remove(int r) {
527 ldout(m_cct, 20) << "r=" << r << dendl;
529 if (r < 0 && r != -ENOENT) {
530 lderr(m_cct) << "failed to remove image journal: " << cpp_strerror(r)
538 send_object_map_remove();
542 void RemoveRequest<I>::send_object_map_remove() {
543 ldout(m_cct, 20) << dendl;
545 using klass = RemoveRequest<I>;
546 librados::AioCompletion *rados_completion =
547 create_rados_callback<klass, &klass::handle_object_map_remove>(this);
549 int r = ObjectMap<>::aio_remove(m_ioctx,
553 rados_completion->release();
557 void RemoveRequest<I>::handle_object_map_remove(int r) {
558 ldout(m_cct, 20) << "r=" << r << dendl;
560 if (r < 0 && r != -ENOENT) {
561 lderr(m_cct) << "failed to remove image journal: " << cpp_strerror(r)
569 mirror_image_remove();
573 void RemoveRequest<I>::mirror_image_remove() {
574 ldout(m_cct, 20) << dendl;
576 librados::ObjectWriteOperation op;
577 cls_client::mirror_image_remove(&op, m_image_id);
579 using klass = RemoveRequest<I>;
580 librados::AioCompletion *rados_completion =
581 create_rados_callback<klass, &klass::handle_mirror_image_remove>(this);
582 int r = m_ioctx.aio_operate(RBD_MIRRORING, rados_completion, &op);
584 rados_completion->release();
588 void RemoveRequest<I>::handle_mirror_image_remove(int r) {
589 ldout(m_cct, 20) << "r=" << r << dendl;
591 if (r < 0 && r != -ENOENT && r != -EOPNOTSUPP) {
592 lderr(m_cct) << "failed to remove mirror image state: "
593 << cpp_strerror(r) << dendl;
598 if (m_from_trash_remove) {
599 // both the id object and the directory entry have been removed in
600 // a previous call to trash_move.
609 void RemoveRequest<I>::remove_image() {
610 ldout(m_cct, 20) << dendl;
612 if (m_old_format || m_unknown_format) {
620 void RemoveRequest<I>::remove_v1_image() {
621 ldout(m_cct, 20) << dendl;
623 Context *ctx = new FunctionContext([this] (int r) {
624 r = tmap_rm(m_ioctx, m_image_name);
625 handle_remove_v1_image(r);
628 m_op_work_queue->queue(ctx, 0);
632 void RemoveRequest<I>::handle_remove_v1_image(int r) {
633 ldout(m_cct, 20) << "r=" << r << dendl;
635 m_old_format = (r == 0);
636 if (r == 0 || (r < 0 && !m_unknown_format)) {
637 if (r < 0 && r != -ENOENT) {
638 lderr(m_cct) << "error removing image from v1 directory: "
639 << cpp_strerror(r) << dendl;
642 m_on_finish->complete(r);
653 void RemoveRequest<I>::remove_v2_image() {
654 ldout(m_cct, 20) << dendl;
656 if (m_image_id.empty()) {
659 } else if (m_image_name.empty()) {
660 dir_get_image_name();
669 void RemoveRequest<I>::dir_get_image_id() {
670 ldout(m_cct, 20) << dendl;
672 librados::ObjectReadOperation op;
673 librbd::cls_client::dir_get_id_start(&op, m_image_name);
675 using klass = RemoveRequest<I>;
676 librados::AioCompletion *rados_completion =
677 create_rados_callback<klass, &klass::handle_dir_get_image_id>(this);
679 int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op, &m_out_bl);
681 rados_completion->release();
685 void RemoveRequest<I>::handle_dir_get_image_id(int r) {
686 ldout(m_cct, 20) << "r=" << r << dendl;
688 if (r < 0 && r != -ENOENT) {
689 lderr(m_cct) << "error fetching image id: " << cpp_strerror(r)
696 bufferlist::iterator iter = m_out_bl.begin();
697 r = librbd::cls_client::dir_get_id_finish(&iter, &m_image_id);
708 void RemoveRequest<I>::dir_get_image_name() {
709 ldout(m_cct, 20) << dendl;
711 librados::ObjectReadOperation op;
712 librbd::cls_client::dir_get_name_start(&op, m_image_id);
714 using klass = RemoveRequest<I>;
715 librados::AioCompletion *rados_completion =
716 create_rados_callback<klass, &klass::handle_dir_get_image_name>(this);
718 int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op, &m_out_bl);
720 rados_completion->release();
724 void RemoveRequest<I>::handle_dir_get_image_name(int r) {
725 ldout(m_cct, 20) << "r=" << r << dendl;
727 if (r < 0 && r != -ENOENT) {
728 lderr(m_cct) << "error fetching image name: " << cpp_strerror(r)
735 bufferlist::iterator iter = m_out_bl.begin();
736 r = librbd::cls_client::dir_get_name_finish(&iter, &m_image_name);
747 void RemoveRequest<I>::remove_id_object() {
748 ldout(m_cct, 20) << dendl;
750 using klass = RemoveRequest<I>;
751 librados::AioCompletion *rados_completion =
752 create_rados_callback<klass, &klass::handle_remove_id_object>(this);
753 int r = m_ioctx.aio_remove(util::id_obj_name(m_image_name), rados_completion);
755 rados_completion->release();
759 void RemoveRequest<I>::handle_remove_id_object(int r) {
760 ldout(m_cct, 20) << "r=" << r << dendl;
762 if (r < 0 && r != -ENOENT) {
763 lderr(m_cct) << "error removing id object: " << cpp_strerror(r)
773 void RemoveRequest<I>::dir_remove_image() {
774 ldout(m_cct, 20) << dendl;
776 librados::ObjectWriteOperation op;
777 librbd::cls_client::dir_remove_image(&op, m_image_name, m_image_id);
779 using klass = RemoveRequest<I>;
780 librados::AioCompletion *rados_completion =
781 create_rados_callback<klass, &klass::handle_dir_remove_image>(this);
782 int r = m_ioctx.aio_operate(RBD_DIRECTORY, rados_completion, &op);
784 rados_completion->release();
788 void RemoveRequest<I>::handle_dir_remove_image(int r) {
789 ldout(m_cct, 20) << "r=" << r << dendl;
791 if (r < 0 && r != -ENOENT) {
792 lderr(m_cct) << "error removing image from v2 directory: "
793 << cpp_strerror(r) << dendl;
800 void RemoveRequest<I>::finish(int r) {
801 ldout(m_cct, 20) << "r=" << r << dendl;
803 m_on_finish->complete(r);
808 } // namespace librbd
810 template class librbd::image::RemoveRequest<librbd::ImageCtx>;