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/Utils.h"
16 #include "librbd/api/Config.h"
17 #include "librbd/api/Trash.h"
18 #include "librbd/image/CloneRequest.h"
19 #include "librbd/image/RemoveRequest.h"
20 #include "librbd/image/PreRemoveRequest.h"
21 #include <boost/scope_exit.hpp>
23 #define dout_subsys ceph_subsys_rbd
25 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
32 bool compare_by_pool(const librbd::linked_image_spec_t
& lhs
,
33 const librbd::linked_image_spec_t
& rhs
)
35 if (lhs
.pool_id
!= rhs
.pool_id
) {
36 return lhs
.pool_id
< rhs
.pool_id
;
37 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
38 return lhs
.pool_namespace
< rhs
.pool_namespace
;
43 bool compare(const librbd::linked_image_spec_t
& lhs
,
44 const librbd::linked_image_spec_t
& rhs
)
46 if (lhs
.pool_name
!= rhs
.pool_name
) {
47 return lhs
.pool_name
< rhs
.pool_name
;
48 } else if (lhs
.pool_id
!= rhs
.pool_id
) {
49 return lhs
.pool_id
< rhs
.pool_id
;
50 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
51 return lhs
.pool_namespace
< rhs
.pool_namespace
;
52 } else if (lhs
.image_name
!= rhs
.image_name
) {
53 return lhs
.image_name
< rhs
.image_name
;
54 } else if (lhs
.image_id
!= rhs
.image_id
) {
55 return lhs
.image_id
< rhs
.image_id
;
61 int pre_remove_image(librados::IoCtx
& io_ctx
, const std::string
& image_id
) {
62 I
*image_ctx
= I::create("", image_id
, nullptr, io_ctx
, false);
63 int r
= image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
69 auto req
= image::PreRemoveRequest
<I
>::create(image_ctx
, false, &ctx
);
73 image_ctx
->state
->close();
77 } // anonymous namespace
80 int Image
<I
>::get_op_features(I
*ictx
, uint64_t *op_features
) {
81 CephContext
*cct
= ictx
->cct
;
82 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
84 int r
= ictx
->state
->refresh_if_required();
89 RWLock::RLocker
snap_locker(ictx
->snap_lock
);
90 *op_features
= ictx
->op_features
;
95 int Image
<I
>::list_images(librados::IoCtx
& io_ctx
,
96 std::vector
<image_spec_t
> *images
) {
97 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
98 ldout(cct
, 20) << "list " << &io_ctx
<< dendl
;
103 if (io_ctx
.get_namespace().empty()) {
105 r
= io_ctx
.read(RBD_DIRECTORY
, bl
, 0, 0);
109 lderr(cct
) << "error listing v1 images: " << cpp_strerror(r
) << dendl
;
113 // V1 format images are in a tmap
115 auto p
= bl
.cbegin();
117 std::map
<std::string
, bufferlist
> m
;
121 images
->push_back({.id
="", .name
= it
.first
});
127 std::map
<std::string
, std::string
> image_names_to_ids
;
128 r
= list_images_v2(io_ctx
, &image_names_to_ids
);
130 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
134 for (const auto& img_pair
: image_names_to_ids
) {
135 images
->push_back({.id
= img_pair
.second
,
136 .name
= img_pair
.first
});
139 // include V2 images in a partially removed state
140 std::vector
<librbd::trash_image_info_t
> trash_images
;
141 r
= Trash
<I
>::list(io_ctx
, trash_images
, false);
142 if (r
< 0 && r
!= -EOPNOTSUPP
) {
143 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
) << dendl
;
147 for (const auto& trash_image
: trash_images
) {
148 if (trash_image
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
149 images
->push_back({.id
= trash_image
.id
,
150 .name
= trash_image
.name
});
158 template <typename I
>
159 int Image
<I
>::list_images_v2(librados::IoCtx
& io_ctx
, ImageNameToIds
*images
) {
160 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
161 ldout(cct
, 20) << "io_ctx=" << &io_ctx
<< dendl
;
163 // new format images are accessed by class methods
166 string last_read
= "";
168 map
<string
, string
> images_page
;
169 r
= cls_client::dir_list(&io_ctx
, RBD_DIRECTORY
, last_read
, max_read
,
171 if (r
< 0 && r
!= -ENOENT
) {
172 lderr(cct
) << "error listing image in directory: "
173 << cpp_strerror(r
) << dendl
;
175 } else if (r
== -ENOENT
) {
178 for (map
<string
, string
>::const_iterator it
= images_page
.begin();
179 it
!= images_page
.end(); ++it
) {
182 if (!images_page
.empty()) {
183 last_read
= images_page
.rbegin()->first
;
185 r
= images_page
.size();
186 } while (r
== max_read
);
191 template <typename I
>
192 int Image
<I
>::get_parent(I
*ictx
,
193 librbd::linked_image_spec_t
*parent_image
,
194 librbd::snap_spec_t
*parent_snap
) {
195 auto cct
= ictx
->cct
;
196 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
198 int r
= ictx
->state
->refresh_if_required();
203 RWLock::RLocker
snap_locker(ictx
->snap_lock
);
204 RWLock::RLocker
parent_locker(ictx
->parent_lock
);
206 bool release_parent_locks
= false;
207 BOOST_SCOPE_EXIT_ALL(ictx
, &release_parent_locks
) {
208 if (release_parent_locks
) {
209 ictx
->parent
->parent_lock
.put_read();
210 ictx
->parent
->snap_lock
.put_read();
214 // if a migration is in-progress, the true parent is the parent
215 // of the migration source image
216 auto parent
= ictx
->parent
;
217 if (!ictx
->migration_info
.empty() && ictx
->parent
!= nullptr) {
218 release_parent_locks
= true;
219 ictx
->parent
->snap_lock
.get_read();
220 ictx
->parent
->parent_lock
.get_read();
222 parent
= ictx
->parent
->parent
;
225 if (parent
== nullptr) {
229 parent_image
->pool_id
= parent
->md_ctx
.get_id();
230 parent_image
->pool_name
= parent
->md_ctx
.get_pool_name();
231 parent_image
->pool_namespace
= parent
->md_ctx
.get_namespace();
233 RWLock::RLocker
parent_snap_locker(parent
->snap_lock
);
234 parent_snap
->id
= parent
->snap_id
;
235 parent_snap
->namespace_type
= RBD_SNAP_NAMESPACE_TYPE_USER
;
236 if (parent
->snap_id
!= CEPH_NOSNAP
) {
237 auto snap_info
= parent
->get_snap_info(parent
->snap_id
);
238 if (snap_info
== nullptr) {
239 lderr(cct
) << "error finding parent snap name: " << cpp_strerror(r
)
244 parent_snap
->namespace_type
= static_cast<snap_namespace_type_t
>(
245 cls::rbd::get_snap_namespace_type(snap_info
->snap_namespace
));
246 parent_snap
->name
= snap_info
->name
;
249 parent_image
->image_id
= parent
->id
;
250 parent_image
->image_name
= parent
->name
;
251 parent_image
->trash
= true;
253 librbd::trash_image_info_t trash_info
;
254 r
= Trash
<I
>::get(parent
->md_ctx
, parent
->id
, &trash_info
);
255 if (r
== -ENOENT
|| r
== -EOPNOTSUPP
) {
256 parent_image
->trash
= false;
258 lderr(cct
) << "error looking up trash status: " << cpp_strerror(r
)
266 template <typename I
>
267 int Image
<I
>::list_children(I
*ictx
,
268 std::vector
<librbd::linked_image_spec_t
> *images
) {
270 return list_descendants(ictx
, 1, images
);
273 template <typename I
>
274 int Image
<I
>::list_children(I
*ictx
,
275 const cls::rbd::ParentImageSpec
&parent_spec
,
276 std::vector
<librbd::linked_image_spec_t
> *images
) {
278 return list_descendants(ictx
, parent_spec
, 1, images
);
281 template <typename I
>
282 int Image
<I
>::list_descendants(
283 librados::IoCtx
& io_ctx
, const std::string
&image_id
,
284 const std::optional
<size_t> &max_level
,
285 std::vector
<librbd::linked_image_spec_t
> *images
) {
286 ImageCtx
*ictx
= new librbd::ImageCtx("", image_id
, nullptr,
288 int r
= ictx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
293 lderr(ictx
->cct
) << "failed to open descendant " << image_id
294 << " from pool " << io_ctx
.get_pool_name() << ":"
295 << cpp_strerror(r
) << dendl
;
299 r
= list_descendants(ictx
, max_level
, images
);
301 int r1
= ictx
->state
->close();
303 lderr(ictx
->cct
) << "error when closing descendant " << image_id
304 << " from pool " << io_ctx
.get_pool_name() << ":"
305 << cpp_strerror(r
) << dendl
;
311 template <typename I
>
312 int Image
<I
>::list_descendants(
313 I
*ictx
, const std::optional
<size_t> &max_level
,
314 std::vector
<librbd::linked_image_spec_t
> *images
) {
315 RWLock::RLocker
l(ictx
->snap_lock
);
316 std::vector
<librados::snap_t
> snap_ids
;
317 if (ictx
->snap_id
!= CEPH_NOSNAP
) {
318 snap_ids
.push_back(ictx
->snap_id
);
320 snap_ids
= ictx
->snaps
;
322 for (auto snap_id
: snap_ids
) {
323 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
324 ictx
->md_ctx
.get_namespace(),
326 int r
= list_descendants(ictx
, parent_spec
, max_level
, images
);
334 template <typename I
>
335 int Image
<I
>::list_descendants(
336 I
*ictx
, const cls::rbd::ParentImageSpec
&parent_spec
,
337 const std::optional
<size_t> &max_level
,
338 std::vector
<librbd::linked_image_spec_t
> *images
) {
339 auto child_max_level
= max_level
;
340 if (child_max_level
) {
341 if (child_max_level
== 0) {
344 (*child_max_level
)--;
346 CephContext
*cct
= ictx
->cct
;
347 ldout(cct
, 20) << "ictx=" << ictx
<< dendl
;
349 // no children for non-layered or old format image
350 if (!ictx
->test_features(RBD_FEATURE_LAYERING
, ictx
->snap_lock
)) {
354 librados::Rados
rados(ictx
->md_ctx
);
356 // search all pools for clone v1 children dependent on this snapshot
357 std::list
<std::pair
<int64_t, std::string
> > pools
;
358 int r
= rados
.pool_list2(pools
);
360 lderr(cct
) << "error listing pools: " << cpp_strerror(r
) << dendl
;
364 for (auto& it
: pools
) {
366 r
= rados
.pool_get_base_tier(it
.first
, &base_tier
);
368 ldout(cct
, 1) << "pool " << it
.second
<< " no longer exists" << dendl
;
371 lderr(cct
) << "error retrieving base tier for pool " << it
.second
375 if (it
.first
!= base_tier
) {
376 // pool is a cache; skip it
381 r
= util::create_ioctx(ictx
->md_ctx
, "child image", it
.first
, {}, &ioctx
);
388 std::set
<std::string
> image_ids
;
389 r
= cls_client::get_children(&ioctx
, RBD_CHILDREN
, parent_spec
,
391 if (r
< 0 && r
!= -ENOENT
) {
392 lderr(cct
) << "error reading list of children from pool " << it
.second
397 for (auto& image_id
: image_ids
) {
399 it
.first
, "", ictx
->md_ctx
.get_namespace(), image_id
, "", false});
400 r
= list_descendants(ictx
->md_ctx
, image_id
, child_max_level
, images
);
407 // retrieve clone v2 children attached to this snapshot
409 r
= util::create_ioctx(ictx
->md_ctx
, "parent image", parent_spec
.pool_id
,
410 parent_spec
.pool_namespace
, &parent_io_ctx
);
415 cls::rbd::ChildImageSpecs child_images
;
416 r
= cls_client::children_list(&parent_io_ctx
,
417 util::header_name(parent_spec
.image_id
),
418 parent_spec
.snap_id
, &child_images
);
419 if (r
< 0 && r
!= -ENOENT
&& r
!= -EOPNOTSUPP
) {
420 lderr(cct
) << "error retrieving children: " << cpp_strerror(r
) << dendl
;
424 for (auto& child_image
: child_images
) {
426 child_image
.pool_id
, "", child_image
.pool_namespace
,
427 child_image
.image_id
, "", false});
428 if (!child_max_level
|| *child_max_level
> 0) {
430 r
= util::create_ioctx(ictx
->md_ctx
, "child image", child_image
.pool_id
,
431 child_image
.pool_namespace
, &ioctx
);
437 r
= list_descendants(ioctx
, child_image
.image_id
, child_max_level
,
445 // batch lookups by pool + namespace
446 std::sort(images
->begin(), images
->end(), compare_by_pool
);
448 int64_t child_pool_id
= -1;
449 librados::IoCtx child_io_ctx
;
450 std::map
<std::string
, std::pair
<std::string
, bool>> child_image_id_to_info
;
451 for (auto& image
: *images
) {
452 if (child_pool_id
== -1 || child_pool_id
!= image
.pool_id
||
453 child_io_ctx
.get_namespace() != image
.pool_namespace
) {
454 r
= util::create_ioctx(ictx
->md_ctx
, "child image", image
.pool_id
,
455 image
.pool_namespace
, &child_io_ctx
);
459 child_pool_id
= image
.pool_id
;
461 child_image_id_to_info
.clear();
463 std::map
<std::string
, std::string
> image_names_to_ids
;
464 r
= list_images_v2(child_io_ctx
, &image_names_to_ids
);
466 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
470 for (auto& [name
, id
] : image_names_to_ids
) {
471 child_image_id_to_info
.insert({id
, {name
, false}});
474 std::vector
<librbd::trash_image_info_t
> trash_images
;
475 r
= Trash
<I
>::list(child_io_ctx
, trash_images
, false);
476 if (r
< 0 && r
!= -EOPNOTSUPP
) {
477 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
)
482 for (auto& it
: trash_images
) {
483 child_image_id_to_info
.insert({
486 it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
? false : true}});
490 auto it
= child_image_id_to_info
.find(image
.image_id
);
491 if (it
== child_image_id_to_info
.end()) {
492 lderr(cct
) << "error looking up name for image id "
493 << image
.image_id
<< " in pool "
494 << child_io_ctx
.get_pool_name()
495 << (image
.pool_namespace
.empty() ?
496 "" : "/" + image
.pool_namespace
) << dendl
;
500 image
.pool_name
= child_io_ctx
.get_pool_name();
501 image
.image_name
= it
->second
.first
;
502 image
.trash
= it
->second
.second
;
505 // final sort by pool + image names
506 std::sort(images
->begin(), images
->end(), compare
);
510 template <typename I
>
511 int Image
<I
>::deep_copy(I
*src
, librados::IoCtx
& dest_md_ctx
,
512 const char *destname
, ImageOptions
& opts
,
513 ProgressContext
&prog_ctx
) {
514 CephContext
*cct
= (CephContext
*)dest_md_ctx
.cct();
515 ldout(cct
, 20) << src
->name
516 << (src
->snap_name
.length() ? "@" + src
->snap_name
: "")
517 << " -> " << destname
<< " opts = " << opts
<< dendl
;
522 RWLock::RLocker
snap_locker(src
->snap_lock
);
524 if (!src
->migration_info
.empty()) {
525 lderr(cct
) << "cannot deep copy migrating image" << dendl
;
529 features
= (src
->features
& ~RBD_FEATURES_IMPLICIT_ENABLE
);
530 src_size
= src
->get_image_size(src
->snap_id
);
533 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
534 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
537 lderr(cct
) << "old format not supported for destination image" << dendl
;
540 uint64_t stripe_unit
= src
->stripe_unit
;
541 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
542 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
544 uint64_t stripe_count
= src
->stripe_count
;
545 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
546 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
548 uint64_t order
= src
->order
;
549 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
550 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
552 if (opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
) != 0) {
553 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
555 if (features
& ~RBD_FEATURES_ALL
) {
556 lderr(cct
) << "librbd does not support requested features" << dendl
;
560 uint64_t flatten
= 0;
561 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
562 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
565 cls::rbd::ParentImageSpec parent_spec
;
567 parent_spec
.pool_id
= -1;
569 RWLock::RLocker
snap_locker(src
->snap_lock
);
570 RWLock::RLocker
parent_locker(src
->parent_lock
);
572 // use oldest snapshot or HEAD for parent spec
573 if (!src
->snap_info
.empty()) {
574 parent_spec
= src
->snap_info
.begin()->second
.parent
.spec
;
576 parent_spec
= src
->parent_md
.spec
;
581 if (parent_spec
.pool_id
== -1) {
582 r
= create(dest_md_ctx
, destname
, "", src_size
, opts
, "", "", false);
584 librados::IoCtx parent_io_ctx
;
585 r
= util::create_ioctx(src
->md_ctx
, "parent image", parent_spec
.pool_id
,
586 parent_spec
.pool_namespace
, &parent_io_ctx
);
591 ConfigProxy config
{cct
->_conf
};
592 api::Config
<I
>::apply_pool_overrides(dest_md_ctx
, &config
);
595 std::string dest_id
= util::generate_image_id(dest_md_ctx
);
596 auto *req
= image::CloneRequest
<I
>::create(
597 config
, parent_io_ctx
, parent_spec
.image_id
, "", parent_spec
.snap_id
,
598 dest_md_ctx
, destname
, dest_id
, opts
, "", "", src
->op_work_queue
, &ctx
);
603 lderr(cct
) << "header creation failed" << dendl
;
606 opts
.set(RBD_IMAGE_OPTION_ORDER
, static_cast<uint64_t>(order
));
608 auto dest
= new I(destname
, "", nullptr, dest_md_ctx
, false);
609 r
= dest
->state
->open(0);
611 lderr(cct
) << "failed to read newly created header" << dendl
;
615 C_SaferCond lock_ctx
;
617 RWLock::WLocker
locker(dest
->owner_lock
);
619 if (dest
->exclusive_lock
== nullptr ||
620 dest
->exclusive_lock
->is_lock_owner()) {
621 lock_ctx
.complete(0);
623 dest
->exclusive_lock
->acquire_lock(&lock_ctx
);
629 lderr(cct
) << "failed to request exclusive lock: " << cpp_strerror(r
)
631 dest
->state
->close();
635 r
= deep_copy(src
, dest
, flatten
> 0, prog_ctx
);
637 int close_r
= dest
->state
->close();
638 if (r
== 0 && close_r
< 0) {
644 template <typename I
>
645 int Image
<I
>::deep_copy(I
*src
, I
*dest
, bool flatten
,
646 ProgressContext
&prog_ctx
) {
647 CephContext
*cct
= src
->cct
;
648 librados::snap_t snap_id_start
= 0;
649 librados::snap_t snap_id_end
;
651 RWLock::RLocker
snap_locker(src
->snap_lock
);
652 snap_id_end
= src
->snap_id
;
655 ThreadPool
*thread_pool
;
656 ContextWQ
*op_work_queue
;
657 ImageCtx::get_thread_pool_instance(cct
, &thread_pool
, &op_work_queue
);
661 auto req
= DeepCopyRequest
<I
>::create(src
, dest
, snap_id_start
, snap_id_end
,
662 flatten
, boost::none
, op_work_queue
,
663 &snap_seqs
, &prog_ctx
, &cond
);
673 template <typename I
>
674 int Image
<I
>::snap_set(I
*ictx
,
675 const cls::rbd::SnapshotNamespace
&snap_namespace
,
676 const char *snap_name
) {
677 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " snap = "
678 << (snap_name
? snap_name
: "NULL") << dendl
;
680 // ignore return value, since we may be set to a non-existent
681 // snapshot and the user is trying to fix that
682 ictx
->state
->refresh_if_required();
684 uint64_t snap_id
= CEPH_NOSNAP
;
685 std::string
name(snap_name
== nullptr ? "" : snap_name
);
687 RWLock::RLocker
snap_locker(ictx
->snap_lock
);
688 snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace
{},
690 if (snap_id
== CEPH_NOSNAP
) {
695 return snap_set(ictx
, snap_id
);
698 template <typename I
>
699 int Image
<I
>::snap_set(I
*ictx
, uint64_t snap_id
) {
700 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " "
701 << "snap_id=" << snap_id
<< dendl
;
703 // ignore return value, since we may be set to a non-existent
704 // snapshot and the user is trying to fix that
705 ictx
->state
->refresh_if_required();
708 ictx
->state
->snap_set(snap_id
, &ctx
);
712 lderr(ictx
->cct
) << "failed to " << (snap_id
== CEPH_NOSNAP
? "un" : "")
713 << "set snapshot: " << cpp_strerror(r
) << dendl
;
721 template <typename I
>
722 int Image
<I
>::remove(IoCtx
& io_ctx
, const std::string
&image_name
,
723 ProgressContext
& prog_ctx
)
725 CephContext
*cct((CephContext
*)io_ctx
.cct());
726 ldout(cct
, 20) << "name=" << image_name
<< dendl
;
728 // look up the V2 image id based on the image name
729 std::string image_id
;
730 int r
= cls_client::dir_get_id(&io_ctx
, RBD_DIRECTORY
, image_name
,
733 // check if it already exists in trash from an aborted trash remove attempt
734 std::vector
<trash_image_info_t
> trash_entries
;
735 r
= Trash
<I
>::list(io_ctx
, trash_entries
, false);
739 for (auto& entry
: trash_entries
) {
740 if (entry
.name
== image_name
&&
741 entry
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
742 return Trash
<I
>::remove(io_ctx
, entry
.id
, true, prog_ctx
);
747 // fall-through if we failed to locate the image in the V2 directory and
750 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(r
) << dendl
;
753 // attempt to move the image to the trash (and optionally immediately
755 ConfigProxy
config(cct
->_conf
);
756 Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
758 rbd_trash_image_source_t trash_image_source
=
759 RBD_TRASH_IMAGE_SOURCE_REMOVING
;
760 uint64_t expire_seconds
= 0;
761 if (config
.get_val
<bool>("rbd_move_to_trash_on_remove")) {
762 // keep the image in the trash upon remove requests
763 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER
;
764 expire_seconds
= config
.get_val
<uint64_t>(
765 "rbd_move_to_trash_on_remove_expire_seconds");
767 // attempt to pre-validate the removal before moving to trash and
769 r
= pre_remove_image
<I
>(io_ctx
, image_id
);
770 if (r
< 0 && r
!= -ENOENT
) {
775 r
= Trash
<I
>::move(io_ctx
, trash_image_source
, image_name
, image_id
,
778 if (trash_image_source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
779 // proceed with attempting to immediately remove the image
780 r
= Trash
<I
>::remove(io_ctx
, image_id
, true, prog_ctx
);
782 if (r
== -ENOTEMPTY
|| r
== -EBUSY
|| r
== -EMLINK
) {
783 // best-effort try to restore the image if the removal
784 // failed for possible expected reasons
785 Trash
<I
>::restore(io_ctx
, trash_image_source
, image_id
, image_name
);
789 } else if (r
< 0 && r
!= -EOPNOTSUPP
) {
793 // fall-through if trash isn't supported
796 ThreadPool
*thread_pool
;
797 ContextWQ
*op_work_queue
;
798 ImageCtx::get_thread_pool_instance(cct
, &thread_pool
, &op_work_queue
);
800 // might be a V1 image format that cannot be moved to the trash
801 // and would not have been listed in the V2 directory -- or the OSDs
802 // are too old and don't support the trash feature
804 auto req
= librbd::image::RemoveRequest
<I
>::create(
805 io_ctx
, image_name
, "", false, false, prog_ctx
, op_work_queue
, &cond
);
812 } // namespace librbd
814 template class librbd::api::Image
<librbd::ImageCtx
>;