From: Ilya Dryomov Date: Wed, 29 Jan 2014 14:12:01 +0000 (+0200) Subject: rbd: check for watchers before trimming an image on 'rbd rm' X-Git-Tag: v0.67.8~41^2 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F1411%2Fhead;p=ceph.git 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) --- 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)