1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/api/Image.h"
5 #include "include/rados/librados.hpp"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "common/Cond.h"
9 #include "cls/rbd/cls_rbd_client.h"
10 #include "librbd/DeepCopyRequest.h"
11 #include "librbd/ExclusiveLock.h"
12 #include "librbd/ImageCtx.h"
13 #include "librbd/ImageState.h"
14 #include "librbd/internal.h"
15 #include "librbd/Operations.h"
16 #include "librbd/Utils.h"
17 #include "librbd/api/Config.h"
18 #include "librbd/api/Trash.h"
19 #include "librbd/image/CloneRequest.h"
20 #include "librbd/image/RemoveRequest.h"
21 #include "librbd/image/PreRemoveRequest.h"
22 #include <boost/scope_exit.hpp>
24 #define dout_subsys ceph_subsys_rbd
26 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
28 using librados::snap_t
;
35 bool compare_by_pool(const librbd::linked_image_spec_t
& lhs
,
36 const librbd::linked_image_spec_t
& rhs
)
38 if (lhs
.pool_id
!= rhs
.pool_id
) {
39 return lhs
.pool_id
< rhs
.pool_id
;
40 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
41 return lhs
.pool_namespace
< rhs
.pool_namespace
;
46 bool compare(const librbd::linked_image_spec_t
& lhs
,
47 const librbd::linked_image_spec_t
& rhs
)
49 if (lhs
.pool_name
!= rhs
.pool_name
) {
50 return lhs
.pool_name
< rhs
.pool_name
;
51 } else if (lhs
.pool_id
!= rhs
.pool_id
) {
52 return lhs
.pool_id
< rhs
.pool_id
;
53 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
54 return lhs
.pool_namespace
< rhs
.pool_namespace
;
55 } else if (lhs
.image_name
!= rhs
.image_name
) {
56 return lhs
.image_name
< rhs
.image_name
;
57 } else if (lhs
.image_id
!= rhs
.image_id
) {
58 return lhs
.image_id
< rhs
.image_id
;
64 int pre_remove_image(librados::IoCtx
& io_ctx
, const std::string
& image_id
) {
65 I
*image_ctx
= I::create("", image_id
, nullptr, io_ctx
, false);
66 int r
= image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
72 auto req
= image::PreRemoveRequest
<I
>::create(image_ctx
, false, &ctx
);
76 image_ctx
->state
->close();
80 } // anonymous namespace
83 int64_t Image
<I
>::get_data_pool_id(I
*ictx
) {
84 if (ictx
->data_ctx
.is_valid()) {
85 return ictx
->data_ctx
.get_id();
89 int r
= cls_client::get_data_pool(&ictx
->md_ctx
, ictx
->header_oid
, &pool_id
);
91 CephContext
*cct
= ictx
->cct
;
92 lderr(cct
) << "error getting data pool ID: " << cpp_strerror(r
) << dendl
;
100 int Image
<I
>::get_op_features(I
*ictx
, uint64_t *op_features
) {
101 CephContext
*cct
= ictx
->cct
;
102 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
104 int r
= ictx
->state
->refresh_if_required();
109 std::shared_lock image_locker
{ictx
->image_lock
};
110 *op_features
= ictx
->op_features
;
114 template <typename I
>
115 int Image
<I
>::list_images(librados::IoCtx
& io_ctx
,
116 std::vector
<image_spec_t
> *images
) {
117 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
118 ldout(cct
, 20) << "list " << &io_ctx
<< dendl
;
123 if (io_ctx
.get_namespace().empty()) {
125 r
= io_ctx
.read(RBD_DIRECTORY
, bl
, 0, 0);
129 lderr(cct
) << "error listing v1 images: " << cpp_strerror(r
) << dendl
;
133 // V1 format images are in a tmap
135 auto p
= bl
.cbegin();
137 std::map
<std::string
, bufferlist
> m
;
141 images
->push_back({.id
="", .name
= it
.first
});
147 std::map
<std::string
, std::string
> image_names_to_ids
;
148 r
= list_images_v2(io_ctx
, &image_names_to_ids
);
150 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
154 for (const auto& img_pair
: image_names_to_ids
) {
155 images
->push_back({.id
= img_pair
.second
,
156 .name
= img_pair
.first
});
159 // include V2 images in a partially removed state
160 std::vector
<librbd::trash_image_info_t
> trash_images
;
161 r
= Trash
<I
>::list(io_ctx
, trash_images
, false);
162 if (r
< 0 && r
!= -EOPNOTSUPP
) {
163 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
) << dendl
;
167 for (const auto& trash_image
: trash_images
) {
168 if (trash_image
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
169 images
->push_back({.id
= trash_image
.id
,
170 .name
= trash_image
.name
});
178 template <typename I
>
179 int Image
<I
>::list_images_v2(librados::IoCtx
& io_ctx
, ImageNameToIds
*images
) {
180 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
181 ldout(cct
, 20) << "io_ctx=" << &io_ctx
<< dendl
;
183 // new format images are accessed by class methods
186 string last_read
= "";
188 map
<string
, string
> images_page
;
189 r
= cls_client::dir_list(&io_ctx
, RBD_DIRECTORY
, last_read
, max_read
,
191 if (r
< 0 && r
!= -ENOENT
) {
192 lderr(cct
) << "error listing image in directory: "
193 << cpp_strerror(r
) << dendl
;
195 } else if (r
== -ENOENT
) {
198 for (map
<string
, string
>::const_iterator it
= images_page
.begin();
199 it
!= images_page
.end(); ++it
) {
202 if (!images_page
.empty()) {
203 last_read
= images_page
.rbegin()->first
;
205 r
= images_page
.size();
206 } while (r
== max_read
);
211 template <typename I
>
212 int Image
<I
>::get_parent(I
*ictx
,
213 librbd::linked_image_spec_t
*parent_image
,
214 librbd::snap_spec_t
*parent_snap
) {
215 auto cct
= ictx
->cct
;
216 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
218 int r
= ictx
->state
->refresh_if_required();
223 std::shared_lock image_locker
{ictx
->image_lock
};
225 bool release_image_lock
= false;
226 BOOST_SCOPE_EXIT_ALL(ictx
, &release_image_lock
) {
227 if (release_image_lock
) {
228 ictx
->parent
->image_lock
.unlock_shared();
232 // if a migration is in-progress, the true parent is the parent
233 // of the migration source image
234 auto parent
= ictx
->parent
;
235 if (!ictx
->migration_info
.empty() && ictx
->parent
!= nullptr) {
236 release_image_lock
= true;
237 ictx
->parent
->image_lock
.lock_shared();
239 parent
= ictx
->parent
->parent
;
242 if (parent
== nullptr) {
246 parent_image
->pool_id
= parent
->md_ctx
.get_id();
247 parent_image
->pool_name
= parent
->md_ctx
.get_pool_name();
248 parent_image
->pool_namespace
= parent
->md_ctx
.get_namespace();
250 std::shared_lock parent_image_locker
{parent
->image_lock
};
251 parent_snap
->id
= parent
->snap_id
;
252 parent_snap
->namespace_type
= RBD_SNAP_NAMESPACE_TYPE_USER
;
253 if (parent
->snap_id
!= CEPH_NOSNAP
) {
254 auto snap_info
= parent
->get_snap_info(parent
->snap_id
);
255 if (snap_info
== nullptr) {
256 lderr(cct
) << "error finding parent snap name: " << cpp_strerror(r
)
261 parent_snap
->namespace_type
= static_cast<snap_namespace_type_t
>(
262 cls::rbd::get_snap_namespace_type(snap_info
->snap_namespace
));
263 parent_snap
->name
= snap_info
->name
;
266 parent_image
->image_id
= parent
->id
;
267 parent_image
->image_name
= parent
->name
;
268 parent_image
->trash
= true;
270 librbd::trash_image_info_t trash_info
;
271 r
= Trash
<I
>::get(parent
->md_ctx
, parent
->id
, &trash_info
);
272 if (r
== -ENOENT
|| r
== -EOPNOTSUPP
) {
273 parent_image
->trash
= false;
275 lderr(cct
) << "error looking up trash status: " << cpp_strerror(r
)
283 template <typename I
>
284 int Image
<I
>::list_children(I
*ictx
,
285 std::vector
<librbd::linked_image_spec_t
> *images
) {
287 return list_descendants(ictx
, 1, images
);
290 template <typename I
>
291 int Image
<I
>::list_children(I
*ictx
,
292 const cls::rbd::ParentImageSpec
&parent_spec
,
293 std::vector
<librbd::linked_image_spec_t
> *images
) {
295 return list_descendants(ictx
, parent_spec
, 1, images
);
298 template <typename I
>
299 int Image
<I
>::list_descendants(
300 librados::IoCtx
& io_ctx
, const std::string
&image_id
,
301 const std::optional
<size_t> &max_level
,
302 std::vector
<librbd::linked_image_spec_t
> *images
) {
303 ImageCtx
*ictx
= new librbd::ImageCtx("", image_id
, nullptr,
305 int r
= ictx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
310 lderr(ictx
->cct
) << "failed to open descendant " << image_id
311 << " from pool " << io_ctx
.get_pool_name() << ":"
312 << cpp_strerror(r
) << dendl
;
316 r
= list_descendants(ictx
, max_level
, images
);
318 int r1
= ictx
->state
->close();
320 lderr(ictx
->cct
) << "error when closing descendant " << image_id
321 << " from pool " << io_ctx
.get_pool_name() << ":"
322 << cpp_strerror(r
) << dendl
;
328 template <typename I
>
329 int Image
<I
>::list_descendants(
330 I
*ictx
, const std::optional
<size_t> &max_level
,
331 std::vector
<librbd::linked_image_spec_t
> *images
) {
332 std::shared_lock l
{ictx
->image_lock
};
333 std::vector
<librados::snap_t
> snap_ids
;
334 if (ictx
->snap_id
!= CEPH_NOSNAP
) {
335 snap_ids
.push_back(ictx
->snap_id
);
337 snap_ids
= ictx
->snaps
;
339 for (auto snap_id
: snap_ids
) {
340 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
341 ictx
->md_ctx
.get_namespace(),
343 int r
= list_descendants(ictx
, parent_spec
, max_level
, images
);
351 template <typename I
>
352 int Image
<I
>::list_descendants(
353 I
*ictx
, const cls::rbd::ParentImageSpec
&parent_spec
,
354 const std::optional
<size_t> &max_level
,
355 std::vector
<librbd::linked_image_spec_t
> *images
) {
356 auto child_max_level
= max_level
;
357 if (child_max_level
) {
358 if (child_max_level
== 0) {
361 (*child_max_level
)--;
363 CephContext
*cct
= ictx
->cct
;
364 ldout(cct
, 20) << "ictx=" << ictx
<< dendl
;
366 // no children for non-layered or old format image
367 if (!ictx
->test_features(RBD_FEATURE_LAYERING
, ictx
->image_lock
)) {
371 librados::Rados
rados(ictx
->md_ctx
);
373 // search all pools for clone v1 children dependent on this snapshot
374 std::list
<std::pair
<int64_t, std::string
> > pools
;
375 int r
= rados
.pool_list2(pools
);
377 lderr(cct
) << "error listing pools: " << cpp_strerror(r
) << dendl
;
381 for (auto& it
: pools
) {
383 r
= rados
.pool_get_base_tier(it
.first
, &base_tier
);
385 ldout(cct
, 1) << "pool " << it
.second
<< " no longer exists" << dendl
;
388 lderr(cct
) << "error retrieving base tier for pool " << it
.second
392 if (it
.first
!= base_tier
) {
393 // pool is a cache; skip it
398 r
= util::create_ioctx(ictx
->md_ctx
, "child image", it
.first
, {}, &ioctx
);
405 std::set
<std::string
> image_ids
;
406 r
= cls_client::get_children(&ioctx
, RBD_CHILDREN
, parent_spec
,
408 if (r
< 0 && r
!= -ENOENT
) {
409 lderr(cct
) << "error reading list of children from pool " << it
.second
414 for (auto& image_id
: image_ids
) {
416 it
.first
, "", ictx
->md_ctx
.get_namespace(), image_id
, "", false});
417 r
= list_descendants(ioctx
, image_id
, child_max_level
, images
);
424 // retrieve clone v2 children attached to this snapshot
426 r
= util::create_ioctx(ictx
->md_ctx
, "parent image", parent_spec
.pool_id
,
427 parent_spec
.pool_namespace
, &parent_io_ctx
);
432 cls::rbd::ChildImageSpecs child_images
;
433 r
= cls_client::children_list(&parent_io_ctx
,
434 util::header_name(parent_spec
.image_id
),
435 parent_spec
.snap_id
, &child_images
);
436 if (r
< 0 && r
!= -ENOENT
&& r
!= -EOPNOTSUPP
) {
437 lderr(cct
) << "error retrieving children: " << cpp_strerror(r
) << dendl
;
441 for (auto& child_image
: child_images
) {
443 child_image
.pool_id
, "", child_image
.pool_namespace
,
444 child_image
.image_id
, "", false});
445 if (!child_max_level
|| *child_max_level
> 0) {
447 r
= util::create_ioctx(ictx
->md_ctx
, "child image", child_image
.pool_id
,
448 child_image
.pool_namespace
, &ioctx
);
454 r
= list_descendants(ioctx
, child_image
.image_id
, child_max_level
,
462 // batch lookups by pool + namespace
463 std::sort(images
->begin(), images
->end(), compare_by_pool
);
465 int64_t child_pool_id
= -1;
466 librados::IoCtx child_io_ctx
;
467 std::map
<std::string
, std::pair
<std::string
, bool>> child_image_id_to_info
;
468 for (auto& image
: *images
) {
469 if (child_pool_id
== -1 || child_pool_id
!= image
.pool_id
||
470 child_io_ctx
.get_namespace() != image
.pool_namespace
) {
471 r
= util::create_ioctx(ictx
->md_ctx
, "child image", image
.pool_id
,
472 image
.pool_namespace
, &child_io_ctx
);
474 image
.pool_name
= "";
475 image
.image_name
= "";
480 child_pool_id
= image
.pool_id
;
482 child_image_id_to_info
.clear();
484 std::map
<std::string
, std::string
> image_names_to_ids
;
485 r
= list_images_v2(child_io_ctx
, &image_names_to_ids
);
487 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
491 for (auto& [name
, id
] : image_names_to_ids
) {
492 child_image_id_to_info
.insert({id
, {name
, false}});
495 std::vector
<librbd::trash_image_info_t
> trash_images
;
496 r
= Trash
<I
>::list(child_io_ctx
, trash_images
, false);
497 if (r
< 0 && r
!= -EOPNOTSUPP
) {
498 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
)
503 for (auto& it
: trash_images
) {
504 child_image_id_to_info
.insert({
507 it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
? false : true}});
511 auto it
= child_image_id_to_info
.find(image
.image_id
);
512 if (it
== child_image_id_to_info
.end()) {
513 lderr(cct
) << "error looking up name for image id "
514 << image
.image_id
<< " in pool "
515 << child_io_ctx
.get_pool_name()
516 << (image
.pool_namespace
.empty() ?
517 "" : "/" + image
.pool_namespace
) << dendl
;
521 image
.pool_name
= child_io_ctx
.get_pool_name();
522 image
.image_name
= it
->second
.first
;
523 image
.trash
= it
->second
.second
;
526 // final sort by pool + image names
527 std::sort(images
->begin(), images
->end(), compare
);
531 template <typename I
>
532 int Image
<I
>::deep_copy(I
*src
, librados::IoCtx
& dest_md_ctx
,
533 const char *destname
, ImageOptions
& opts
,
534 ProgressContext
&prog_ctx
) {
535 CephContext
*cct
= (CephContext
*)dest_md_ctx
.cct();
536 ldout(cct
, 20) << src
->name
537 << (src
->snap_name
.length() ? "@" + src
->snap_name
: "")
538 << " -> " << destname
<< " opts = " << opts
<< dendl
;
543 std::shared_lock image_locker
{src
->image_lock
};
545 if (!src
->migration_info
.empty()) {
546 lderr(cct
) << "cannot deep copy migrating image" << dendl
;
550 features
= (src
->features
& ~RBD_FEATURES_IMPLICIT_ENABLE
);
551 src_size
= src
->get_image_size(src
->snap_id
);
554 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
555 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
558 lderr(cct
) << "old format not supported for destination image" << dendl
;
561 uint64_t stripe_unit
= src
->stripe_unit
;
562 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
563 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
565 uint64_t stripe_count
= src
->stripe_count
;
566 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
567 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
569 uint64_t order
= src
->order
;
570 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
571 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
573 if (opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
) != 0) {
574 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
576 if (features
& ~RBD_FEATURES_ALL
) {
577 lderr(cct
) << "librbd does not support requested features" << dendl
;
581 uint64_t flatten
= 0;
582 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
583 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
586 cls::rbd::ParentImageSpec parent_spec
;
588 parent_spec
.pool_id
= -1;
590 std::shared_lock image_locker
{src
->image_lock
};
592 // use oldest snapshot or HEAD for parent spec
593 if (!src
->snap_info
.empty()) {
594 parent_spec
= src
->snap_info
.begin()->second
.parent
.spec
;
596 parent_spec
= src
->parent_md
.spec
;
601 if (parent_spec
.pool_id
== -1) {
602 r
= create(dest_md_ctx
, destname
, "", src_size
, opts
, "", "", false);
604 librados::IoCtx parent_io_ctx
;
605 r
= util::create_ioctx(src
->md_ctx
, "parent image", parent_spec
.pool_id
,
606 parent_spec
.pool_namespace
, &parent_io_ctx
);
611 ConfigProxy config
{cct
->_conf
};
612 api::Config
<I
>::apply_pool_overrides(dest_md_ctx
, &config
);
615 std::string dest_id
= util::generate_image_id(dest_md_ctx
);
616 auto *req
= image::CloneRequest
<I
>::create(
617 config
, parent_io_ctx
, parent_spec
.image_id
, "", {}, parent_spec
.snap_id
,
618 dest_md_ctx
, destname
, dest_id
, opts
, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
,
619 "", "", src
->op_work_queue
, &ctx
);
624 lderr(cct
) << "header creation failed" << dendl
;
627 opts
.set(RBD_IMAGE_OPTION_ORDER
, static_cast<uint64_t>(order
));
629 auto dest
= new I(destname
, "", nullptr, dest_md_ctx
, false);
630 r
= dest
->state
->open(0);
632 lderr(cct
) << "failed to read newly created header" << dendl
;
636 C_SaferCond lock_ctx
;
638 std::unique_lock locker
{dest
->owner_lock
};
640 if (dest
->exclusive_lock
== nullptr ||
641 dest
->exclusive_lock
->is_lock_owner()) {
642 lock_ctx
.complete(0);
644 dest
->exclusive_lock
->acquire_lock(&lock_ctx
);
650 lderr(cct
) << "failed to request exclusive lock: " << cpp_strerror(r
)
652 dest
->state
->close();
656 r
= deep_copy(src
, dest
, flatten
> 0, prog_ctx
);
658 int close_r
= dest
->state
->close();
659 if (r
== 0 && close_r
< 0) {
665 template <typename I
>
666 int Image
<I
>::deep_copy(I
*src
, I
*dest
, bool flatten
,
667 ProgressContext
&prog_ctx
) {
668 CephContext
*cct
= src
->cct
;
669 librados::snap_t snap_id_start
= 0;
670 librados::snap_t snap_id_end
;
672 std::shared_lock image_locker
{src
->image_lock
};
673 snap_id_end
= src
->snap_id
;
676 ThreadPool
*thread_pool
;
677 ContextWQ
*op_work_queue
;
678 ImageCtx::get_thread_pool_instance(cct
, &thread_pool
, &op_work_queue
);
682 auto req
= DeepCopyRequest
<I
>::create(src
, dest
, snap_id_start
, snap_id_end
,
683 0U, flatten
, boost::none
, op_work_queue
,
684 &snap_seqs
, &prog_ctx
, &cond
);
694 template <typename I
>
695 int Image
<I
>::snap_set(I
*ictx
,
696 const cls::rbd::SnapshotNamespace
&snap_namespace
,
697 const char *snap_name
) {
698 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " snap = "
699 << (snap_name
? snap_name
: "NULL") << dendl
;
701 // ignore return value, since we may be set to a non-existent
702 // snapshot and the user is trying to fix that
703 ictx
->state
->refresh_if_required();
705 uint64_t snap_id
= CEPH_NOSNAP
;
706 std::string
name(snap_name
== nullptr ? "" : snap_name
);
708 std::shared_lock image_locker
{ictx
->image_lock
};
709 snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace
{},
711 if (snap_id
== CEPH_NOSNAP
) {
716 return snap_set(ictx
, snap_id
);
719 template <typename I
>
720 int Image
<I
>::snap_set(I
*ictx
, uint64_t snap_id
) {
721 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " "
722 << "snap_id=" << snap_id
<< dendl
;
724 // ignore return value, since we may be set to a non-existent
725 // snapshot and the user is trying to fix that
726 ictx
->state
->refresh_if_required();
729 ictx
->state
->snap_set(snap_id
, &ctx
);
733 lderr(ictx
->cct
) << "failed to " << (snap_id
== CEPH_NOSNAP
? "un" : "")
734 << "set snapshot: " << cpp_strerror(r
) << dendl
;
742 template <typename I
>
743 int Image
<I
>::remove(IoCtx
& io_ctx
, const std::string
&image_name
,
744 ProgressContext
& prog_ctx
)
746 CephContext
*cct((CephContext
*)io_ctx
.cct());
747 ldout(cct
, 20) << "name=" << image_name
<< dendl
;
749 // look up the V2 image id based on the image name
750 std::string image_id
;
751 int r
= cls_client::dir_get_id(&io_ctx
, RBD_DIRECTORY
, image_name
,
754 // check if it already exists in trash from an aborted trash remove attempt
755 std::vector
<trash_image_info_t
> trash_entries
;
756 r
= Trash
<I
>::list(io_ctx
, trash_entries
, false);
760 for (auto& entry
: trash_entries
) {
761 if (entry
.name
== image_name
&&
762 entry
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
763 return Trash
<I
>::remove(io_ctx
, entry
.id
, true, prog_ctx
);
768 // fall-through if we failed to locate the image in the V2 directory and
771 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(r
) << dendl
;
774 // attempt to move the image to the trash (and optionally immediately
776 ConfigProxy
config(cct
->_conf
);
777 Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
779 rbd_trash_image_source_t trash_image_source
=
780 RBD_TRASH_IMAGE_SOURCE_REMOVING
;
781 uint64_t expire_seconds
= 0;
782 if (config
.get_val
<bool>("rbd_move_to_trash_on_remove")) {
783 // keep the image in the trash upon remove requests
784 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER
;
785 expire_seconds
= config
.get_val
<uint64_t>(
786 "rbd_move_to_trash_on_remove_expire_seconds");
788 // attempt to pre-validate the removal before moving to trash and
790 r
= pre_remove_image
<I
>(io_ctx
, image_id
);
792 if (config
.get_val
<bool>("rbd_move_parent_to_trash_on_remove")) {
793 // keep the image in the trash until the last child is removed
794 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER_PARENT
;
796 lderr(cct
) << "image has snapshots - not removing" << dendl
;
799 } else if (r
< 0 && r
!= -ENOENT
) {
804 r
= Trash
<I
>::move(io_ctx
, trash_image_source
, image_name
, image_id
,
807 if (trash_image_source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
808 // proceed with attempting to immediately remove the image
809 r
= Trash
<I
>::remove(io_ctx
, image_id
, true, prog_ctx
);
811 if (r
== -ENOTEMPTY
|| r
== -EBUSY
|| r
== -EMLINK
) {
812 // best-effort try to restore the image if the removal
813 // failed for possible expected reasons
814 Trash
<I
>::restore(io_ctx
, {cls::rbd::TRASH_IMAGE_SOURCE_REMOVING
},
815 image_id
, image_name
);
819 } else if (r
< 0 && r
!= -EOPNOTSUPP
) {
823 // fall-through if trash isn't supported
826 ThreadPool
*thread_pool
;
827 ContextWQ
*op_work_queue
;
828 ImageCtx::get_thread_pool_instance(cct
, &thread_pool
, &op_work_queue
);
830 // might be a V1 image format that cannot be moved to the trash
831 // and would not have been listed in the V2 directory -- or the OSDs
832 // are too old and don't support the trash feature
834 auto req
= librbd::image::RemoveRequest
<I
>::create(
835 io_ctx
, image_name
, "", false, false, prog_ctx
, op_work_queue
, &cond
);
841 template <typename I
>
842 int Image
<I
>::flatten_children(I
*ictx
, const char* snap_name
,
843 ProgressContext
& pctx
) {
844 CephContext
*cct
= ictx
->cct
;
845 ldout(cct
, 20) << "children flatten " << ictx
->name
<< dendl
;
847 int r
= ictx
->state
->refresh_if_required();
852 std::shared_lock l
{ictx
->image_lock
};
853 snap_t snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
856 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
857 ictx
->md_ctx
.get_namespace(),
859 std::vector
<librbd::linked_image_spec_t
> child_images
;
860 r
= list_children(ictx
, parent_spec
, &child_images
);
865 size_t size
= child_images
.size();
870 librados::IoCtx child_io_ctx
;
871 int64_t child_pool_id
= -1;
873 for (auto &child_image
: child_images
){
874 std::string pool
= child_image
.pool_name
;
875 if (child_pool_id
== -1 ||
876 child_pool_id
!= child_image
.pool_id
||
877 child_io_ctx
.get_namespace() != child_image
.pool_namespace
) {
878 r
= util::create_ioctx(ictx
->md_ctx
, "child image",
879 child_image
.pool_id
, child_image
.pool_namespace
,
885 child_pool_id
= child_image
.pool_id
;
888 ImageCtx
*imctx
= new ImageCtx("", child_image
.image_id
, nullptr,
889 child_io_ctx
, false);
890 r
= imctx
->state
->open(0);
892 lderr(cct
) << "error opening image: " << cpp_strerror(r
) << dendl
;
896 if ((imctx
->features
& RBD_FEATURE_DEEP_FLATTEN
) == 0 &&
897 !imctx
->snaps
.empty()) {
898 lderr(cct
) << "snapshot in-use by " << pool
<< "/" << imctx
->name
900 imctx
->state
->close();
904 librbd::NoOpProgressContext prog_ctx
;
905 r
= imctx
->operations
->flatten(prog_ctx
);
907 lderr(cct
) << "error flattening image: " << pool
<< "/"
908 << (child_image
.pool_namespace
.empty() ?
909 "" : "/" + child_image
.pool_namespace
)
910 << child_image
.image_name
<< cpp_strerror(r
) << dendl
;
911 imctx
->state
->close();
915 r
= imctx
->state
->close();
917 lderr(cct
) << "failed to close image: " << cpp_strerror(r
) << dendl
;
921 pctx
.update_progress(++i
, size
);
922 ceph_assert(i
<= size
);
929 } // namespace librbd
931 template class librbd::api::Image
<librbd::ImageCtx
>;