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/AsioEngine.h"
11 #include "librbd/DeepCopyRequest.h"
12 #include "librbd/ExclusiveLock.h"
13 #include "librbd/ImageCtx.h"
14 #include "librbd/ImageState.h"
15 #include "librbd/internal.h"
16 #include "librbd/Operations.h"
17 #include "librbd/Utils.h"
18 #include "librbd/api/Config.h"
19 #include "librbd/api/Trash.h"
20 #include "librbd/api/Utils.h"
21 #include "librbd/crypto/FormatRequest.h"
22 #include "librbd/crypto/LoadRequest.h"
23 #include "librbd/deep_copy/Handler.h"
24 #include "librbd/image/CloneRequest.h"
25 #include "librbd/image/RemoveRequest.h"
26 #include "librbd/image/PreRemoveRequest.h"
27 #include "librbd/io/ImageDispatcherInterface.h"
28 #include "librbd/io/ObjectDispatcherInterface.h"
29 #include "librbd/io/AioCompletion.h"
30 #include "librbd/io/ImageDispatchSpec.h"
31 #include <boost/scope_exit.hpp>
33 #define dout_subsys ceph_subsys_rbd
35 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
39 using librados::snap_t
;
46 bool compare_by_pool(const librbd::linked_image_spec_t
& lhs
,
47 const librbd::linked_image_spec_t
& rhs
)
49 if (lhs
.pool_id
!= rhs
.pool_id
) {
50 return lhs
.pool_id
< rhs
.pool_id
;
51 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
52 return lhs
.pool_namespace
< rhs
.pool_namespace
;
57 bool compare(const librbd::linked_image_spec_t
& lhs
,
58 const librbd::linked_image_spec_t
& rhs
)
60 if (lhs
.pool_name
!= rhs
.pool_name
) {
61 return lhs
.pool_name
< rhs
.pool_name
;
62 } else if (lhs
.pool_id
!= rhs
.pool_id
) {
63 return lhs
.pool_id
< rhs
.pool_id
;
64 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
65 return lhs
.pool_namespace
< rhs
.pool_namespace
;
66 } else if (lhs
.image_name
!= rhs
.image_name
) {
67 return lhs
.image_name
< rhs
.image_name
;
68 } else if (lhs
.image_id
!= rhs
.image_id
) {
69 return lhs
.image_id
< rhs
.image_id
;
75 int pre_remove_image(librados::IoCtx
& io_ctx
, const std::string
& image_id
) {
76 I
*image_ctx
= I::create("", image_id
, nullptr, io_ctx
, false);
77 int r
= image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
83 auto req
= image::PreRemoveRequest
<I
>::create(image_ctx
, false, &ctx
);
87 image_ctx
->state
->close();
91 } // anonymous namespace
94 int64_t Image
<I
>::get_data_pool_id(I
*ictx
) {
95 if (ictx
->data_ctx
.is_valid()) {
96 return ictx
->data_ctx
.get_id();
100 int r
= cls_client::get_data_pool(&ictx
->md_ctx
, ictx
->header_oid
, &pool_id
);
102 CephContext
*cct
= ictx
->cct
;
103 lderr(cct
) << "error getting data pool ID: " << cpp_strerror(r
) << dendl
;
110 template <typename I
>
111 int Image
<I
>::get_op_features(I
*ictx
, uint64_t *op_features
) {
112 CephContext
*cct
= ictx
->cct
;
113 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
115 int r
= ictx
->state
->refresh_if_required();
120 std::shared_lock image_locker
{ictx
->image_lock
};
121 *op_features
= ictx
->op_features
;
125 template <typename I
>
126 int Image
<I
>::list_images(librados::IoCtx
& io_ctx
,
127 std::vector
<image_spec_t
> *images
) {
128 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
129 ldout(cct
, 20) << "list " << &io_ctx
<< dendl
;
134 if (io_ctx
.get_namespace().empty()) {
136 r
= io_ctx
.read(RBD_DIRECTORY
, bl
, 0, 0);
140 lderr(cct
) << "error listing v1 images: " << cpp_strerror(r
) << dendl
;
144 // V1 format images are in a tmap
146 auto p
= bl
.cbegin();
148 std::map
<std::string
, bufferlist
> m
;
152 images
->push_back({.id
="", .name
= it
.first
});
158 std::map
<std::string
, std::string
> image_names_to_ids
;
159 r
= list_images_v2(io_ctx
, &image_names_to_ids
);
161 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
165 for (const auto& img_pair
: image_names_to_ids
) {
166 images
->push_back({.id
= img_pair
.second
,
167 .name
= img_pair
.first
});
170 // include V2 images in a partially removed state
171 std::vector
<librbd::trash_image_info_t
> trash_images
;
172 r
= Trash
<I
>::list(io_ctx
, trash_images
, false);
173 if (r
< 0 && r
!= -EOPNOTSUPP
) {
174 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
) << dendl
;
178 for (const auto& trash_image
: trash_images
) {
179 if (trash_image
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
180 images
->push_back({.id
= trash_image
.id
,
181 .name
= trash_image
.name
});
189 template <typename I
>
190 int Image
<I
>::list_images_v2(librados::IoCtx
& io_ctx
, ImageNameToIds
*images
) {
191 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
192 ldout(cct
, 20) << "io_ctx=" << &io_ctx
<< dendl
;
194 // new format images are accessed by class methods
197 string last_read
= "";
199 map
<string
, string
> images_page
;
200 r
= cls_client::dir_list(&io_ctx
, RBD_DIRECTORY
, last_read
, max_read
,
202 if (r
< 0 && r
!= -ENOENT
) {
203 lderr(cct
) << "error listing image in directory: "
204 << cpp_strerror(r
) << dendl
;
206 } else if (r
== -ENOENT
) {
209 for (map
<string
, string
>::const_iterator it
= images_page
.begin();
210 it
!= images_page
.end(); ++it
) {
213 if (!images_page
.empty()) {
214 last_read
= images_page
.rbegin()->first
;
216 r
= images_page
.size();
217 } while (r
== max_read
);
222 template <typename I
>
223 int Image
<I
>::get_parent(I
*ictx
,
224 librbd::linked_image_spec_t
*parent_image
,
225 librbd::snap_spec_t
*parent_snap
) {
226 auto cct
= ictx
->cct
;
227 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
229 int r
= ictx
->state
->refresh_if_required();
234 std::shared_lock image_locker
{ictx
->image_lock
};
236 bool release_image_lock
= false;
237 BOOST_SCOPE_EXIT_ALL(ictx
, &release_image_lock
) {
238 if (release_image_lock
) {
239 ictx
->parent
->image_lock
.unlock_shared();
243 // if a migration is in-progress, the true parent is the parent
244 // of the migration source image
245 auto parent
= ictx
->parent
;
246 if (!ictx
->migration_info
.empty() && ictx
->parent
!= nullptr) {
247 release_image_lock
= true;
248 ictx
->parent
->image_lock
.lock_shared();
250 parent
= ictx
->parent
->parent
;
253 if (parent
== nullptr) {
257 parent_image
->pool_id
= parent
->md_ctx
.get_id();
258 parent_image
->pool_name
= parent
->md_ctx
.get_pool_name();
259 parent_image
->pool_namespace
= parent
->md_ctx
.get_namespace();
261 std::shared_lock parent_image_locker
{parent
->image_lock
};
262 parent_snap
->id
= parent
->snap_id
;
263 parent_snap
->namespace_type
= RBD_SNAP_NAMESPACE_TYPE_USER
;
264 if (parent
->snap_id
!= CEPH_NOSNAP
) {
265 auto snap_info
= parent
->get_snap_info(parent
->snap_id
);
266 if (snap_info
== nullptr) {
267 lderr(cct
) << "error finding parent snap name: " << cpp_strerror(r
)
272 parent_snap
->namespace_type
= static_cast<snap_namespace_type_t
>(
273 cls::rbd::get_snap_namespace_type(snap_info
->snap_namespace
));
274 parent_snap
->name
= snap_info
->name
;
277 parent_image
->image_id
= parent
->id
;
278 parent_image
->image_name
= parent
->name
;
279 parent_image
->trash
= true;
281 librbd::trash_image_info_t trash_info
;
282 r
= Trash
<I
>::get(parent
->md_ctx
, parent
->id
, &trash_info
);
283 if (r
== -ENOENT
|| r
== -EOPNOTSUPP
) {
284 parent_image
->trash
= false;
286 lderr(cct
) << "error looking up trash status: " << cpp_strerror(r
)
294 template <typename I
>
295 int Image
<I
>::list_children(I
*ictx
,
296 std::vector
<librbd::linked_image_spec_t
> *images
) {
298 return list_descendants(ictx
, 1, images
);
301 template <typename I
>
302 int Image
<I
>::list_children(I
*ictx
,
303 const cls::rbd::ParentImageSpec
&parent_spec
,
304 std::vector
<librbd::linked_image_spec_t
> *images
) {
306 return list_descendants(ictx
, parent_spec
, 1, images
);
309 template <typename I
>
310 int Image
<I
>::list_descendants(
311 librados::IoCtx
& io_ctx
, const std::string
&image_id
,
312 const std::optional
<size_t> &max_level
,
313 std::vector
<librbd::linked_image_spec_t
> *images
) {
314 ImageCtx
*ictx
= new librbd::ImageCtx("", image_id
, nullptr,
316 CephContext
*cct
= ictx
->cct
;
317 int r
= ictx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
322 lderr(cct
) << "failed to open descendant " << image_id
323 << " from pool " << io_ctx
.get_pool_name() << ":"
324 << cpp_strerror(r
) << dendl
;
328 r
= list_descendants(ictx
, max_level
, images
);
330 int r1
= ictx
->state
->close();
332 lderr(cct
) << "error when closing descendant " << image_id
333 << " from pool " << io_ctx
.get_pool_name() << ":"
334 << cpp_strerror(r1
) << dendl
;
340 template <typename I
>
341 int Image
<I
>::list_descendants(
342 I
*ictx
, const std::optional
<size_t> &max_level
,
343 std::vector
<librbd::linked_image_spec_t
> *images
) {
344 std::shared_lock l
{ictx
->image_lock
};
345 std::vector
<librados::snap_t
> snap_ids
;
346 if (ictx
->snap_id
!= CEPH_NOSNAP
) {
347 snap_ids
.push_back(ictx
->snap_id
);
349 snap_ids
= ictx
->snaps
;
351 for (auto snap_id
: snap_ids
) {
352 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
353 ictx
->md_ctx
.get_namespace(),
355 int r
= list_descendants(ictx
, parent_spec
, max_level
, images
);
363 template <typename I
>
364 int Image
<I
>::list_descendants(
365 I
*ictx
, const cls::rbd::ParentImageSpec
&parent_spec
,
366 const std::optional
<size_t> &max_level
,
367 std::vector
<librbd::linked_image_spec_t
> *images
) {
368 auto child_max_level
= max_level
;
369 if (child_max_level
) {
370 if (child_max_level
== 0) {
373 (*child_max_level
)--;
375 CephContext
*cct
= ictx
->cct
;
376 ldout(cct
, 20) << "ictx=" << ictx
<< dendl
;
378 // no children for non-layered or old format image
379 if (!ictx
->test_features(RBD_FEATURE_LAYERING
, ictx
->image_lock
)) {
383 librados::Rados
rados(ictx
->md_ctx
);
385 // search all pools for clone v1 children dependent on this snapshot
386 std::list
<std::pair
<int64_t, std::string
> > pools
;
387 int r
= rados
.pool_list2(pools
);
389 lderr(cct
) << "error listing pools: " << cpp_strerror(r
) << dendl
;
393 for (auto& it
: pools
) {
395 r
= rados
.pool_get_base_tier(it
.first
, &base_tier
);
397 ldout(cct
, 1) << "pool " << it
.second
<< " no longer exists" << dendl
;
400 lderr(cct
) << "error retrieving base tier for pool " << it
.second
404 if (it
.first
!= base_tier
) {
405 // pool is a cache; skip it
410 r
= librbd::util::create_ioctx(
411 ictx
->md_ctx
, "child image", it
.first
, {}, &ioctx
);
418 std::set
<std::string
> image_ids
;
419 r
= cls_client::get_children(&ioctx
, RBD_CHILDREN
, parent_spec
,
421 if (r
< 0 && r
!= -ENOENT
) {
422 lderr(cct
) << "error reading list of children from pool " << it
.second
427 for (auto& image_id
: image_ids
) {
429 it
.first
, "", ictx
->md_ctx
.get_namespace(), image_id
, "", false});
430 r
= list_descendants(ioctx
, image_id
, child_max_level
, images
);
437 // retrieve clone v2 children attached to this snapshot
439 r
= librbd::util::create_ioctx(
440 ictx
->md_ctx
, "parent image",parent_spec
.pool_id
,
441 parent_spec
.pool_namespace
, &parent_io_ctx
);
446 cls::rbd::ChildImageSpecs child_images
;
447 r
= cls_client::children_list(
448 &parent_io_ctx
, librbd::util::header_name(parent_spec
.image_id
),
449 parent_spec
.snap_id
, &child_images
);
450 if (r
< 0 && r
!= -ENOENT
&& r
!= -EOPNOTSUPP
) {
451 lderr(cct
) << "error retrieving children: " << cpp_strerror(r
) << dendl
;
455 for (auto& child_image
: child_images
) {
457 child_image
.pool_id
, "", child_image
.pool_namespace
,
458 child_image
.image_id
, "", false});
459 if (!child_max_level
|| *child_max_level
> 0) {
461 r
= librbd::util::create_ioctx(
462 ictx
->md_ctx
, "child image", child_image
.pool_id
,
463 child_image
.pool_namespace
, &ioctx
);
469 r
= list_descendants(ioctx
, child_image
.image_id
, child_max_level
,
477 // batch lookups by pool + namespace
478 std::sort(images
->begin(), images
->end(), compare_by_pool
);
480 int64_t child_pool_id
= -1;
481 librados::IoCtx child_io_ctx
;
482 std::map
<std::string
, std::pair
<std::string
, bool>> child_image_id_to_info
;
483 for (auto& image
: *images
) {
484 if (child_pool_id
== -1 || child_pool_id
!= image
.pool_id
||
485 child_io_ctx
.get_namespace() != image
.pool_namespace
) {
486 r
= librbd::util::create_ioctx(
487 ictx
->md_ctx
, "child image", image
.pool_id
, image
.pool_namespace
,
490 image
.pool_name
= "";
491 image
.image_name
= "";
496 child_pool_id
= image
.pool_id
;
498 child_image_id_to_info
.clear();
500 std::map
<std::string
, std::string
> image_names_to_ids
;
501 r
= list_images_v2(child_io_ctx
, &image_names_to_ids
);
503 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
507 for (auto& [name
, id
] : image_names_to_ids
) {
508 child_image_id_to_info
.insert({id
, {name
, false}});
511 std::vector
<librbd::trash_image_info_t
> trash_images
;
512 r
= Trash
<I
>::list(child_io_ctx
, trash_images
, false);
513 if (r
< 0 && r
!= -EOPNOTSUPP
) {
514 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
)
519 for (auto& it
: trash_images
) {
520 child_image_id_to_info
.insert({
523 it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
? false : true}});
527 auto it
= child_image_id_to_info
.find(image
.image_id
);
528 if (it
== child_image_id_to_info
.end()) {
529 lderr(cct
) << "error looking up name for image id "
530 << image
.image_id
<< " in pool "
531 << child_io_ctx
.get_pool_name()
532 << (image
.pool_namespace
.empty() ?
533 "" : "/" + image
.pool_namespace
) << dendl
;
537 image
.pool_name
= child_io_ctx
.get_pool_name();
538 image
.image_name
= it
->second
.first
;
539 image
.trash
= it
->second
.second
;
542 // final sort by pool + image names
543 std::sort(images
->begin(), images
->end(), compare
);
547 template <typename I
>
548 int Image
<I
>::deep_copy(I
*src
, librados::IoCtx
& dest_md_ctx
,
549 const char *destname
, ImageOptions
& opts
,
550 ProgressContext
&prog_ctx
) {
551 CephContext
*cct
= (CephContext
*)dest_md_ctx
.cct();
552 ldout(cct
, 20) << src
->name
553 << (src
->snap_name
.length() ? "@" + src
->snap_name
: "")
554 << " -> " << destname
<< " opts = " << opts
<< dendl
;
559 std::shared_lock image_locker
{src
->image_lock
};
561 if (!src
->migration_info
.empty()) {
562 lderr(cct
) << "cannot deep copy migrating image" << dendl
;
566 features
= src
->features
;
567 src_size
= src
->get_image_size(src
->snap_id
);
570 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
571 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
574 lderr(cct
) << "old format not supported for destination image" << dendl
;
577 uint64_t stripe_unit
= src
->stripe_unit
;
578 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
579 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
581 uint64_t stripe_count
= src
->stripe_count
;
582 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
583 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
585 uint64_t order
= src
->order
;
586 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
587 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
589 if (opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
) != 0) {
590 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
592 if (features
& ~RBD_FEATURES_ALL
) {
593 lderr(cct
) << "librbd does not support requested features" << dendl
;
597 uint64_t flatten
= 0;
598 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
599 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
602 cls::rbd::ParentImageSpec parent_spec
;
604 parent_spec
.pool_id
= -1;
606 std::shared_lock image_locker
{src
->image_lock
};
608 // use oldest snapshot or HEAD for parent spec
609 if (!src
->snap_info
.empty()) {
610 parent_spec
= src
->snap_info
.begin()->second
.parent
.spec
;
612 parent_spec
= src
->parent_md
.spec
;
617 if (parent_spec
.pool_id
== -1) {
618 r
= create(dest_md_ctx
, destname
, "", src_size
, opts
, "", "", false);
620 librados::IoCtx parent_io_ctx
;
621 r
= librbd::util::create_ioctx(
622 src
->md_ctx
, "parent image", parent_spec
.pool_id
,
623 parent_spec
.pool_namespace
, &parent_io_ctx
);
628 ConfigProxy config
{cct
->_conf
};
629 api::Config
<I
>::apply_pool_overrides(dest_md_ctx
, &config
);
632 std::string dest_id
= librbd::util::generate_image_id(dest_md_ctx
);
633 auto *req
= image::CloneRequest
<I
>::create(
634 config
, parent_io_ctx
, parent_spec
.image_id
, "", {}, parent_spec
.snap_id
,
635 dest_md_ctx
, destname
, dest_id
, opts
, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
,
636 "", "", src
->op_work_queue
, &ctx
);
641 lderr(cct
) << "header creation failed" << dendl
;
644 opts
.set(RBD_IMAGE_OPTION_ORDER
, static_cast<uint64_t>(order
));
646 auto dest
= new I(destname
, "", nullptr, dest_md_ctx
, false);
647 r
= dest
->state
->open(0);
649 lderr(cct
) << "failed to read newly created header" << dendl
;
653 C_SaferCond lock_ctx
;
655 std::unique_lock locker
{dest
->owner_lock
};
657 if (dest
->exclusive_lock
== nullptr ||
658 dest
->exclusive_lock
->is_lock_owner()) {
659 lock_ctx
.complete(0);
661 dest
->exclusive_lock
->acquire_lock(&lock_ctx
);
667 lderr(cct
) << "failed to request exclusive lock: " << cpp_strerror(r
)
669 dest
->state
->close();
673 r
= deep_copy(src
, dest
, flatten
> 0, prog_ctx
);
675 int close_r
= dest
->state
->close();
676 if (r
== 0 && close_r
< 0) {
682 template <typename I
>
683 int Image
<I
>::deep_copy(I
*src
, I
*dest
, bool flatten
,
684 ProgressContext
&prog_ctx
) {
685 // ensure previous writes are visible to dest
686 C_SaferCond flush_ctx
;
688 std::shared_lock owner_locker
{src
->owner_lock
};
689 auto aio_comp
= io::AioCompletion::create_and_start(&flush_ctx
, src
,
691 auto req
= io::ImageDispatchSpec::create_flush(
692 *src
, io::IMAGE_DISPATCH_LAYER_INTERNAL_START
,
693 aio_comp
, io::FLUSH_SOURCE_INTERNAL
, {});
696 int r
= flush_ctx
.wait();
701 librados::snap_t snap_id_start
= 0;
702 librados::snap_t snap_id_end
;
704 std::shared_lock image_locker
{src
->image_lock
};
705 snap_id_end
= src
->snap_id
;
708 AsioEngine
asio_engine(src
->md_ctx
);
712 deep_copy::ProgressHandler progress_handler
{&prog_ctx
};
713 auto req
= DeepCopyRequest
<I
>::create(
714 src
, dest
, snap_id_start
, snap_id_end
, 0U, flatten
, boost::none
,
715 asio_engine
.get_work_queue(), &snap_seqs
, &progress_handler
, &cond
);
725 template <typename I
>
726 int Image
<I
>::snap_set(I
*ictx
,
727 const cls::rbd::SnapshotNamespace
&snap_namespace
,
728 const char *snap_name
) {
729 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " snap = "
730 << (snap_name
? snap_name
: "NULL") << dendl
;
732 // ignore return value, since we may be set to a non-existent
733 // snapshot and the user is trying to fix that
734 ictx
->state
->refresh_if_required();
736 uint64_t snap_id
= CEPH_NOSNAP
;
737 std::string
name(snap_name
== nullptr ? "" : snap_name
);
739 std::shared_lock image_locker
{ictx
->image_lock
};
740 snap_id
= ictx
->get_snap_id(snap_namespace
, snap_name
);
741 if (snap_id
== CEPH_NOSNAP
) {
746 return snap_set(ictx
, snap_id
);
749 template <typename I
>
750 int Image
<I
>::snap_set(I
*ictx
, uint64_t snap_id
) {
751 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " "
752 << "snap_id=" << snap_id
<< dendl
;
754 // ignore return value, since we may be set to a non-existent
755 // snapshot and the user is trying to fix that
756 ictx
->state
->refresh_if_required();
759 ictx
->state
->snap_set(snap_id
, &ctx
);
763 lderr(ictx
->cct
) << "failed to " << (snap_id
== CEPH_NOSNAP
? "un" : "")
764 << "set snapshot: " << cpp_strerror(r
) << dendl
;
772 template <typename I
>
773 int Image
<I
>::remove(IoCtx
& io_ctx
, const std::string
&image_name
,
774 ProgressContext
& prog_ctx
)
776 CephContext
*cct((CephContext
*)io_ctx
.cct());
777 ldout(cct
, 20) << "name=" << image_name
<< dendl
;
779 // look up the V2 image id based on the image name
780 std::string image_id
;
781 int r
= cls_client::dir_get_id(&io_ctx
, RBD_DIRECTORY
, image_name
,
784 // check if it already exists in trash from an aborted trash remove attempt
785 std::vector
<trash_image_info_t
> trash_entries
;
786 r
= Trash
<I
>::list(io_ctx
, trash_entries
, false);
790 for (auto& entry
: trash_entries
) {
791 if (entry
.name
== image_name
&&
792 entry
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
793 cls::rbd::TrashImageSpec spec
;
794 r
= cls_client::trash_get(&io_ctx
, entry
.id
, &spec
);
796 lderr(cct
) << "error getting image id " << entry
.id
797 << " info from trash: " << cpp_strerror(r
) << dendl
;
800 if (spec
.state
== cls::rbd::TRASH_IMAGE_STATE_MOVING
) {
801 r
= Trash
<I
>::move(io_ctx
, entry
.source
, entry
.name
, entry
.id
, 0);
806 return Trash
<I
>::remove(io_ctx
, entry
.id
, true, prog_ctx
);
810 // fall-through if we failed to locate the image in the V2 directory and
813 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(r
) << dendl
;
816 // attempt to move the image to the trash (and optionally immediately
818 ConfigProxy
config(cct
->_conf
);
819 Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
821 rbd_trash_image_source_t trash_image_source
=
822 RBD_TRASH_IMAGE_SOURCE_REMOVING
;
823 uint64_t expire_seconds
= 0;
824 if (config
.get_val
<bool>("rbd_move_to_trash_on_remove")) {
825 // keep the image in the trash upon remove requests
826 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER
;
827 expire_seconds
= config
.get_val
<uint64_t>(
828 "rbd_move_to_trash_on_remove_expire_seconds");
830 // attempt to pre-validate the removal before moving to trash and
832 r
= pre_remove_image
<I
>(io_ctx
, image_id
);
834 if (config
.get_val
<bool>("rbd_move_parent_to_trash_on_remove")) {
835 // keep the image in the trash until the last child is removed
836 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER_PARENT
;
838 lderr(cct
) << "image has snapshots - not removing" << dendl
;
841 } else if (r
< 0 && r
!= -ENOENT
) {
846 r
= Trash
<I
>::move(io_ctx
, trash_image_source
, image_name
, image_id
,
849 if (trash_image_source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
850 // proceed with attempting to immediately remove the image
851 r
= Trash
<I
>::remove(io_ctx
, image_id
, true, prog_ctx
);
853 if (r
== -ENOTEMPTY
|| r
== -EBUSY
|| r
== -EMLINK
) {
854 // best-effort try to restore the image if the removal
855 // failed for possible expected reasons
856 Trash
<I
>::restore(io_ctx
, {cls::rbd::TRASH_IMAGE_SOURCE_REMOVING
},
857 image_id
, image_name
);
861 } else if (r
< 0 && r
!= -EOPNOTSUPP
) {
865 // fall-through if trash isn't supported
868 AsioEngine
asio_engine(io_ctx
);
870 // might be a V1 image format that cannot be moved to the trash
871 // and would not have been listed in the V2 directory -- or the OSDs
872 // are too old and don't support the trash feature
874 auto req
= librbd::image::RemoveRequest
<I
>::create(
875 io_ctx
, image_name
, "", false, false, prog_ctx
,
876 asio_engine
.get_work_queue(), &cond
);
882 template <typename I
>
883 int Image
<I
>::flatten_children(I
*ictx
, const char* snap_name
,
884 ProgressContext
& pctx
) {
885 CephContext
*cct
= ictx
->cct
;
886 ldout(cct
, 20) << "children flatten " << ictx
->name
<< dendl
;
888 int r
= ictx
->state
->refresh_if_required();
893 std::shared_lock l
{ictx
->image_lock
};
894 snap_t snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
897 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
898 ictx
->md_ctx
.get_namespace(),
900 std::vector
<librbd::linked_image_spec_t
> child_images
;
901 r
= list_children(ictx
, parent_spec
, &child_images
);
906 size_t size
= child_images
.size();
911 librados::IoCtx child_io_ctx
;
912 int64_t child_pool_id
= -1;
914 for (auto &child_image
: child_images
){
915 std::string pool
= child_image
.pool_name
;
916 if (child_pool_id
== -1 ||
917 child_pool_id
!= child_image
.pool_id
||
918 child_io_ctx
.get_namespace() != child_image
.pool_namespace
) {
919 r
= librbd::util::create_ioctx(
920 ictx
->md_ctx
, "child image", child_image
.pool_id
,
921 child_image
.pool_namespace
, &child_io_ctx
);
926 child_pool_id
= child_image
.pool_id
;
929 ImageCtx
*imctx
= new ImageCtx("", child_image
.image_id
, nullptr,
930 child_io_ctx
, false);
931 r
= imctx
->state
->open(0);
933 lderr(cct
) << "error opening image: " << cpp_strerror(r
) << dendl
;
937 if ((imctx
->features
& RBD_FEATURE_DEEP_FLATTEN
) == 0 &&
938 !imctx
->snaps
.empty()) {
939 lderr(cct
) << "snapshot in-use by " << pool
<< "/" << imctx
->name
941 imctx
->state
->close();
945 librbd::NoOpProgressContext prog_ctx
;
946 r
= imctx
->operations
->flatten(prog_ctx
);
948 lderr(cct
) << "error flattening image: " << pool
<< "/"
949 << (child_image
.pool_namespace
.empty() ?
950 "" : "/" + child_image
.pool_namespace
)
951 << child_image
.image_name
<< cpp_strerror(r
) << dendl
;
952 imctx
->state
->close();
956 r
= imctx
->state
->close();
958 lderr(cct
) << "failed to close image: " << cpp_strerror(r
) << dendl
;
962 pctx
.update_progress(++i
, size
);
963 ceph_assert(i
<= size
);
969 template <typename I
>
970 int Image
<I
>::encryption_format(I
* ictx
, encryption_format_t format
,
971 encryption_options_t opts
, size_t opts_size
,
973 crypto::EncryptionFormat
<I
>* result_format
;
974 auto r
= util::create_encryption_format(
975 ictx
->cct
, format
, opts
, opts_size
, c_api
, &result_format
);
981 auto req
= librbd::crypto::FormatRequest
<I
>::create(
982 ictx
, std::unique_ptr
<crypto::EncryptionFormat
<I
>>(result_format
),
988 template <typename I
>
989 int Image
<I
>::encryption_load(I
* ictx
, const encryption_spec_t
*specs
,
990 size_t spec_count
, bool c_api
) {
991 std::vector
<std::unique_ptr
<crypto::EncryptionFormat
<I
>>> formats
;
993 for (size_t i
= 0; i
< spec_count
; ++i
) {
994 crypto::EncryptionFormat
<I
>* result_format
;
995 auto r
= util::create_encryption_format(
996 ictx
->cct
, specs
[i
].format
, specs
[i
].opts
, specs
[i
].opts_size
,
997 c_api
, &result_format
);
1002 formats
.emplace_back(result_format
);
1006 auto req
= librbd::crypto::LoadRequest
<I
>::create(
1007 ictx
, std::move(formats
), &cond
);
1013 } // namespace librbd
1015 template class librbd::api::Image
<librbd::ImageCtx
>;