From cd7986caf6baee5f9d6498b113b3382e66dd6f77 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Wed, 29 Jan 2014 16:12:01 +0200 Subject: [PATCH] rbd: check for watchers before trimming an image on 'rbd rm' Check for watchers before trimming image data to try to avoid getting into the following situation: - user does 'rbd rm' on a mapped image with an fs mounted from it - 'rbd rm' trims (removes) all image data, only header is left - 'rbd rm' tries to remove a header and fails because krbd has a watcher registered on the header - at this point image cannot be unmapped because of the mounted fs - fs cannot be unmounted because all its data and metadata is gone Unfortunately, this fix doesn't make it impossible to happen (the required atomicity isn't there), but it's a big improvement over the status quo. Fixes: http://tracker.ceph.com/issues/7076 Reviewed-by: Josh Durgin Signed-off-by: Ilya Dryomov (cherry picked from commit 0a553cfa81b06e75585ab3c39927e307ec0f4cb6) --- src/librbd/internal.cc | 24 ++++++++++++++++++++---- src/test/pybind/test_rbd.py | 4 ++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 1fd7942f3b8e9..e70fe22fc5623 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -1347,15 +1347,31 @@ reprotect_and_return_err: if (r < 0) { ldout(cct, 2) << "error opening image: " << cpp_strerror(-r) << dendl; } else { + string header_oid = ictx->header_oid; + old_format = ictx->old_format; + unknown_format = false; + id = ictx->id; + if (ictx->snaps.size()) { lderr(cct) << "image has snapshots - not removing" << dendl; close_image(ictx); return -ENOTEMPTY; } - string header_oid = ictx->header_oid; - old_format = ictx->old_format; - unknown_format = false; - id = ictx->id; + + std::list watchers; + r = io_ctx.list_watchers(header_oid, &watchers); + if (r < 0) { + lderr(cct) << "error listing watchers" << dendl; + close_image(ictx); + return r; + } + if (watchers.size() > 1) { + lderr(cct) << "image has watchers - not removing" << dendl; + close_image(ictx); + return -EBUSY; + } + assert(watchers.size() == 1); + ictx->md_lock.get_read(); trim_image(ictx, 0, prog_ctx); ictx->md_lock.put_read(); diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 5f14e62bbded5..123d0bff457ff 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -363,7 +363,11 @@ class TestImage(object): self.image.remove_snap('snap1') def test_remove_with_watcher(self): + data = rand_data(256) + self.image.write(data, 0) assert_raises(ImageBusy, remove_image) + read = self.image.read(0, 256) + eq(read, data) def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) -- 2.39.5