Skip to content

Commit 2ae4a57

Browse files
committed
librbd: disallow "rbd trash mv" if image is in a group
Removing an image that is a member of a group has always been disallowed. However, moving an image that is a member of a group to trash is currently allowed and this is deceptive -- the only reason for a user to move an image to trash should be the intent to remove it. More importantly, group APIs operate in terms of image names -- there are no corresponding variants that would operate in terms of image IDs. For example, even though internally GroupImageSpec struct stores an image ID, the public rbd_group_image_info_t struct insists on an image name. When rbd_group_image_list() encounters a trashed member image (i.e. one that doesn't have a name), it just fails with ENOENT and no listing gets produced at all until the offending image is restored from trash. Something like this can be very hard to debug for an average user, so let's make rbd_trash_move() fail with EMLINK the same way as rbd_remove() does in this scenario. The one case where moving a member image to trash makes sense is live migration where the source image gets trashed to be almost immediately replaced by the destination image as part of preparing migration. Fixes: https://tracker.ceph.com/issues/71026 Signed-off-by: Ilya Dryomov <[email protected]>
1 parent 61075e3 commit 2ae4a57

File tree

4 files changed

+26
-8
lines changed

4 files changed

+26
-8
lines changed

PendingReleaseNotes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@
129129
* RGW: Added support for the `RestrictPublicBuckets` property of the S3 `PublicAccessBlock`
130130
configuration.
131131

132+
* RBD: Moving an image that is a member of a group to trash is no longer
133+
allowed. `rbd trash mv` command now behaves the same way as `rbd rm` in this
134+
scenario.
135+
132136
>=19.2.1
133137

134138
* CephFS: Command `fs subvolume create` now allows tagging subvolumes through option

src/librbd/api/Trash.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ int Trash<I>::move(librados::IoCtx &io_ctx, rbd_trash_image_source_t source,
217217
ictx->state->close();
218218
return -EBUSY;
219219
}
220+
if (ictx->group_spec.is_valid() &&
221+
source != RBD_TRASH_IMAGE_SOURCE_MIGRATION) {
222+
lderr(cct) << "image is in a group - not moving to trash" << dendl;
223+
ictx->image_lock.unlock_shared();
224+
ictx->state->close();
225+
return -EMLINK;
226+
}
220227
ictx->image_lock.unlock_shared();
221228

222229
if (mirror_r >= 0 &&

src/test/pybind/test_rbd.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,13 +3069,10 @@ def test_group_image_list(self):
30693069

30703070
def test_group_image_list_move_to_trash(self):
30713071
eq([], list(self.group.list_images()))
3072-
with Image(ioctx, image_name) as image:
3073-
image_id = image.id()
30743072
self.group.add_image(ioctx, image_name)
30753073
eq([image_name], [img['name'] for img in self.group.list_images()])
3076-
RBD().trash_move(ioctx, image_name, 0)
3077-
eq([], list(self.group.list_images()))
3078-
RBD().trash_restore(ioctx, image_id, image_name)
3074+
assert_raises(ImageMemberOfGroup, RBD().trash_move, ioctx, image_name, 0)
3075+
eq([image_name], [img['name'] for img in self.group.list_images()])
30793076

30803077
def test_group_image_list_remove(self):
30813078
# need a closed image to get ImageMemberOfGroup instead of ImageBusy

src/tools/rbd/action/Trash.cc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,15 @@ int execute_move(const po::variables_map &vm,
9797
librbd::RBD rbd;
9898
r = rbd.trash_move(io_ctx, image_name.c_str(), dt);
9999
if (r < 0) {
100-
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
101-
<< std::endl;
100+
if (r == -EMLINK) {
101+
std::cerr << "rbd: error: image belongs to a group"
102+
<< std::endl
103+
<< "Remove the image from the group and try again."
104+
<< std::endl;
105+
} else {
106+
std::cerr << "rbd: deferred delete error: " << cpp_strerror(r)
107+
<< std::endl;
108+
}
102109
return r;
103110
}
104111

@@ -162,7 +169,10 @@ int execute_remove(const po::variables_map &vm,
162169
<< "waiting 30s for the crashed client to timeout."
163170
<< std::endl;
164171
} else if (r == -EMLINK) {
165-
std::cerr << std::endl
172+
// moving to trash an image that belongs to a group is no longer
173+
// allowed, this is to handle any image that was trashed earlier
174+
std::cerr << "rbd: error: image belongs to a group"
175+
<< std::endl
166176
<< "Remove the image from the group and try again."
167177
<< std::endl;
168178
} else if (r == -EPERM) {

0 commit comments

Comments
 (0)