summaryrefslogtreecommitdiffstats
path: root/src/librbd/image/PreRemoveRequest.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/librbd/image/PreRemoveRequest.cc348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/librbd/image/PreRemoveRequest.cc b/src/librbd/image/PreRemoveRequest.cc
new file mode 100644
index 000000000..fa4141834
--- /dev/null
+++ b/src/librbd/image/PreRemoveRequest.cc
@@ -0,0 +1,348 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "librbd/image/PreRemoveRequest.h"
+#include "common/dout.h"
+#include "common/errno.h"
+#include "cls/rbd/cls_rbd_types.h"
+#include "librbd/ExclusiveLock.h"
+#include "librbd/Utils.h"
+#include "librbd/exclusive_lock/StandardPolicy.h"
+#include "librbd/image/ListWatchersRequest.h"
+#include "librbd/journal/DisabledPolicy.h"
+#include "librbd/operation/SnapshotRemoveRequest.h"
+
+#define dout_subsys ceph_subsys_rbd
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::image::PreRemoveRequest: " << this \
+ << " " << __func__ << ": "
+
+namespace librbd {
+namespace image {
+
+namespace {
+
+bool auto_delete_snapshot(const SnapInfo& snap_info) {
+ auto snap_namespace_type = cls::rbd::get_snap_namespace_type(
+ snap_info.snap_namespace);
+ switch (snap_namespace_type) {
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool ignore_snapshot(const SnapInfo& snap_info) {
+ auto snap_namespace_type = cls::rbd::get_snap_namespace_type(
+ snap_info.snap_namespace);
+ switch (snap_namespace_type) {
+ case cls::rbd::SNAPSHOT_NAMESPACE_TYPE_MIRROR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // anonymous namespace
+
+using util::create_context_callback;
+using util::create_rados_callback;
+
+template <typename I>
+void PreRemoveRequest<I>::send() {
+ auto cct = m_image_ctx->cct;
+ if (m_image_ctx->operations_disabled) {
+ lderr(cct) << "image operations disabled due to unsupported op features"
+ << dendl;
+ finish(-EROFS);
+ return;
+ }
+
+ acquire_exclusive_lock();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::acquire_exclusive_lock() {
+ // lock for write for set_exclusive_lock_policy()
+ std::unique_lock owner_locker{m_image_ctx->owner_lock};
+ if (m_image_ctx->exclusive_lock == nullptr) {
+ owner_locker.unlock();
+ validate_image_removal();
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ // refuse to release exclusive lock when (in the midst of) removing
+ // the image
+ m_image_ctx->set_exclusive_lock_policy(
+ new exclusive_lock::StandardPolicy<I>(m_image_ctx));
+
+ // do not attempt to open the journal when removing the image in case
+ // it's corrupt
+ if (m_image_ctx->test_features(RBD_FEATURE_JOURNALING)) {
+ std::unique_lock image_locker{m_image_ctx->image_lock};
+ m_image_ctx->set_journal_policy(new journal::DisabledPolicy());
+ }
+
+ m_exclusive_lock = m_image_ctx->exclusive_lock;
+
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>,
+ &PreRemoveRequest<I>::handle_exclusive_lock>(this, m_exclusive_lock);
+ m_exclusive_lock->acquire_lock(ctx);
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_exclusive_lock(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0 || !m_image_ctx->exclusive_lock->is_lock_owner()) {
+ if (!m_force) {
+ lderr(cct) << "cannot obtain exclusive lock - not removing" << dendl;
+ finish(-EBUSY);
+ } else {
+ ldout(cct, 5) << "cannot obtain exclusive lock - "
+ << "proceeding due to force flag set" << dendl;
+ shut_down_exclusive_lock();
+ }
+ return;
+ }
+
+ validate_image_removal();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::shut_down_exclusive_lock() {
+ std::shared_lock owner_locker{m_image_ctx->owner_lock};
+ if (m_image_ctx->exclusive_lock == nullptr) {
+ owner_locker.unlock();
+ validate_image_removal();
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>,
+ &PreRemoveRequest<I>::handle_shut_down_exclusive_lock>(this);
+
+ m_exclusive_lock = m_image_ctx->exclusive_lock;
+ m_exclusive_lock->shut_down(ctx);
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_shut_down_exclusive_lock(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ m_exclusive_lock->put();
+ m_exclusive_lock = nullptr;
+
+ if (r < 0) {
+ lderr(cct) << "error shutting down exclusive lock: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ ceph_assert(m_image_ctx->exclusive_lock == nullptr);
+ validate_image_removal();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::validate_image_removal() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ if (!m_image_ctx->ignore_migrating &&
+ m_image_ctx->test_features(RBD_FEATURE_MIGRATING)) {
+ lderr(cct) << "image in migration state - not removing" << dendl;
+ finish(-EBUSY);
+ return;
+ }
+
+ check_image_snaps();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_image_snaps() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ m_image_ctx->image_lock.lock_shared();
+ for (auto& snap_info : m_image_ctx->snap_info) {
+ if (auto_delete_snapshot(snap_info.second)) {
+ m_snap_infos.insert(snap_info);
+ } else if (!ignore_snapshot(snap_info.second)) {
+ m_image_ctx->image_lock.unlock_shared();
+
+ ldout(cct, 5) << "image has snapshots - not removing" << dendl;
+ finish(-ENOTEMPTY);
+ return;
+ }
+ }
+ m_image_ctx->image_lock.unlock_shared();
+
+ list_image_watchers();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::list_image_watchers() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ int flags = LIST_WATCHERS_FILTER_OUT_MY_INSTANCE |
+ LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES;
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>,
+ &PreRemoveRequest<I>::handle_list_image_watchers>(this);
+ auto req = ListWatchersRequest<I>::create(*m_image_ctx, flags, &m_watchers,
+ ctx);
+ req->send();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_list_image_watchers(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r < 0) {
+ lderr(cct) << "error listing image watchers: " << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ check_image_watchers();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_image_watchers() {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ if (!m_watchers.empty()) {
+ lderr(cct) << "image has watchers - not removing" << dendl;
+ finish(-EBUSY);
+ return;
+ }
+
+ check_group();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::check_group() {
+ if (m_image_ctx->old_format) {
+ finish(0);
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << dendl;
+
+ librados::ObjectReadOperation op;
+ librbd::cls_client::image_group_get_start(&op);
+
+ auto rados_completion = create_rados_callback<
+ PreRemoveRequest<I>, &PreRemoveRequest<I>::handle_check_group>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid,
+ rados_completion, &op, &m_out_bl);
+ ceph_assert(r == 0);
+ rados_completion->release();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_check_group(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ cls::rbd::GroupSpec s;
+ if (r == 0) {
+ auto it = m_out_bl.cbegin();
+ r = librbd::cls_client::image_group_get_finish(&it, &s);
+ }
+ if (r < 0 && r != -EOPNOTSUPP) {
+ lderr(cct) << "error fetching group for image: " << cpp_strerror(r)
+ << dendl;
+ finish(r);
+ return;
+ }
+
+ if (s.is_valid()) {
+ lderr(cct) << "image is in a group - not removing" << dendl;
+ finish(-EMLINK);
+ return;
+ }
+
+ remove_snapshot();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::remove_snapshot() {
+ if (m_snap_infos.empty()) {
+ finish(0);
+ return;
+ }
+
+ auto cct = m_image_ctx->cct;
+ auto snap_id = m_snap_infos.begin()->first;
+ auto& snap_info = m_snap_infos.begin()->second;
+ ldout(cct, 20) << "snap_id=" << snap_id << ", "
+ << "snap_name=" << snap_info.name << dendl;
+
+ std::shared_lock owner_lock{m_image_ctx->owner_lock};
+ auto ctx = create_context_callback<
+ PreRemoveRequest<I>, &PreRemoveRequest<I>::handle_remove_snapshot>(this);
+ auto req = librbd::operation::SnapshotRemoveRequest<I>::create(
+ *m_image_ctx, snap_info.snap_namespace, snap_info.name,
+ snap_id, ctx);
+ req->send();
+
+}
+
+template <typename I>
+void PreRemoveRequest<I>::handle_remove_snapshot(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (r == -EBUSY) {
+ ldout(cct, 5) << "skipping attached child" << dendl;
+ if (m_ret_val == 0) {
+ m_ret_val = -ECHILD;
+ }
+ } else if (r < 0 && r != -ENOENT) {
+ auto snap_id = m_snap_infos.begin()->first;
+ lderr(cct) << "failed to auto-prune snapshot " << snap_id << ": "
+ << cpp_strerror(r) << dendl;
+ finish(r);
+ return;
+ }
+
+ ceph_assert(!m_snap_infos.empty());
+ m_snap_infos.erase(m_snap_infos.begin());
+
+ remove_snapshot();
+}
+
+template <typename I>
+void PreRemoveRequest<I>::finish(int r) {
+ auto cct = m_image_ctx->cct;
+ ldout(cct, 5) << "r=" << r << dendl;
+
+ if (m_ret_val == 0) {
+ m_ret_val = r;
+ }
+
+ m_on_finish->complete(m_ret_val);
+ delete this;
+}
+
+} // namespace image
+} // namespace librbd
+
+template class librbd::image::PreRemoveRequest<librbd::ImageCtx>;