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 <boost/scope_exit.hpp>
31 #define dout_subsys ceph_subsys_rbd
33 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
35 using librados::snap_t
;
42 bool compare_by_pool(const librbd::linked_image_spec_t
& lhs
,
43 const librbd::linked_image_spec_t
& rhs
)
45 if (lhs
.pool_id
!= rhs
.pool_id
) {
46 return lhs
.pool_id
< rhs
.pool_id
;
47 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
48 return lhs
.pool_namespace
< rhs
.pool_namespace
;
53 bool compare(const librbd::linked_image_spec_t
& lhs
,
54 const librbd::linked_image_spec_t
& rhs
)
56 if (lhs
.pool_name
!= rhs
.pool_name
) {
57 return lhs
.pool_name
< rhs
.pool_name
;
58 } else if (lhs
.pool_id
!= rhs
.pool_id
) {
59 return lhs
.pool_id
< rhs
.pool_id
;
60 } else if (lhs
.pool_namespace
!= rhs
.pool_namespace
) {
61 return lhs
.pool_namespace
< rhs
.pool_namespace
;
62 } else if (lhs
.image_name
!= rhs
.image_name
) {
63 return lhs
.image_name
< rhs
.image_name
;
64 } else if (lhs
.image_id
!= rhs
.image_id
) {
65 return lhs
.image_id
< rhs
.image_id
;
71 int pre_remove_image(librados::IoCtx
& io_ctx
, const std::string
& image_id
) {
72 I
*image_ctx
= I::create("", image_id
, nullptr, io_ctx
, false);
73 int r
= image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
79 auto req
= image::PreRemoveRequest
<I
>::create(image_ctx
, false, &ctx
);
83 image_ctx
->state
->close();
87 } // anonymous namespace
90 int64_t Image
<I
>::get_data_pool_id(I
*ictx
) {
91 if (ictx
->data_ctx
.is_valid()) {
92 return ictx
->data_ctx
.get_id();
96 int r
= cls_client::get_data_pool(&ictx
->md_ctx
, ictx
->header_oid
, &pool_id
);
98 CephContext
*cct
= ictx
->cct
;
99 lderr(cct
) << "error getting data pool ID: " << cpp_strerror(r
) << dendl
;
106 template <typename I
>
107 int Image
<I
>::get_op_features(I
*ictx
, uint64_t *op_features
) {
108 CephContext
*cct
= ictx
->cct
;
109 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
111 int r
= ictx
->state
->refresh_if_required();
116 std::shared_lock image_locker
{ictx
->image_lock
};
117 *op_features
= ictx
->op_features
;
121 template <typename I
>
122 int Image
<I
>::list_images(librados::IoCtx
& io_ctx
,
123 std::vector
<image_spec_t
> *images
) {
124 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
125 ldout(cct
, 20) << "list " << &io_ctx
<< dendl
;
130 if (io_ctx
.get_namespace().empty()) {
132 r
= io_ctx
.read(RBD_DIRECTORY
, bl
, 0, 0);
136 lderr(cct
) << "error listing v1 images: " << cpp_strerror(r
) << dendl
;
140 // V1 format images are in a tmap
142 auto p
= bl
.cbegin();
144 std::map
<std::string
, bufferlist
> m
;
148 images
->push_back({.id
="", .name
= it
.first
});
154 std::map
<std::string
, std::string
> image_names_to_ids
;
155 r
= list_images_v2(io_ctx
, &image_names_to_ids
);
157 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
161 for (const auto& img_pair
: image_names_to_ids
) {
162 images
->push_back({.id
= img_pair
.second
,
163 .name
= img_pair
.first
});
166 // include V2 images in a partially removed state
167 std::vector
<librbd::trash_image_info_t
> trash_images
;
168 r
= Trash
<I
>::list(io_ctx
, trash_images
, false);
169 if (r
< 0 && r
!= -EOPNOTSUPP
) {
170 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
) << dendl
;
174 for (const auto& trash_image
: trash_images
) {
175 if (trash_image
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
176 images
->push_back({.id
= trash_image
.id
,
177 .name
= trash_image
.name
});
185 template <typename I
>
186 int Image
<I
>::list_images_v2(librados::IoCtx
& io_ctx
, ImageNameToIds
*images
) {
187 CephContext
*cct
= (CephContext
*)io_ctx
.cct();
188 ldout(cct
, 20) << "io_ctx=" << &io_ctx
<< dendl
;
190 // new format images are accessed by class methods
193 string last_read
= "";
195 map
<string
, string
> images_page
;
196 r
= cls_client::dir_list(&io_ctx
, RBD_DIRECTORY
, last_read
, max_read
,
198 if (r
< 0 && r
!= -ENOENT
) {
199 lderr(cct
) << "error listing image in directory: "
200 << cpp_strerror(r
) << dendl
;
202 } else if (r
== -ENOENT
) {
205 for (map
<string
, string
>::const_iterator it
= images_page
.begin();
206 it
!= images_page
.end(); ++it
) {
209 if (!images_page
.empty()) {
210 last_read
= images_page
.rbegin()->first
;
212 r
= images_page
.size();
213 } while (r
== max_read
);
218 template <typename I
>
219 int Image
<I
>::get_parent(I
*ictx
,
220 librbd::linked_image_spec_t
*parent_image
,
221 librbd::snap_spec_t
*parent_snap
) {
222 auto cct
= ictx
->cct
;
223 ldout(cct
, 20) << "image_ctx=" << ictx
<< dendl
;
225 int r
= ictx
->state
->refresh_if_required();
230 std::shared_lock image_locker
{ictx
->image_lock
};
232 bool release_image_lock
= false;
233 BOOST_SCOPE_EXIT_ALL(ictx
, &release_image_lock
) {
234 if (release_image_lock
) {
235 ictx
->parent
->image_lock
.unlock_shared();
239 // if a migration is in-progress, the true parent is the parent
240 // of the migration source image
241 auto parent
= ictx
->parent
;
242 if (!ictx
->migration_info
.empty() && ictx
->parent
!= nullptr) {
243 release_image_lock
= true;
244 ictx
->parent
->image_lock
.lock_shared();
246 parent
= ictx
->parent
->parent
;
249 if (parent
== nullptr) {
253 parent_image
->pool_id
= parent
->md_ctx
.get_id();
254 parent_image
->pool_name
= parent
->md_ctx
.get_pool_name();
255 parent_image
->pool_namespace
= parent
->md_ctx
.get_namespace();
257 std::shared_lock parent_image_locker
{parent
->image_lock
};
258 parent_snap
->id
= parent
->snap_id
;
259 parent_snap
->namespace_type
= RBD_SNAP_NAMESPACE_TYPE_USER
;
260 if (parent
->snap_id
!= CEPH_NOSNAP
) {
261 auto snap_info
= parent
->get_snap_info(parent
->snap_id
);
262 if (snap_info
== nullptr) {
263 lderr(cct
) << "error finding parent snap name: " << cpp_strerror(r
)
268 parent_snap
->namespace_type
= static_cast<snap_namespace_type_t
>(
269 cls::rbd::get_snap_namespace_type(snap_info
->snap_namespace
));
270 parent_snap
->name
= snap_info
->name
;
273 parent_image
->image_id
= parent
->id
;
274 parent_image
->image_name
= parent
->name
;
275 parent_image
->trash
= true;
277 librbd::trash_image_info_t trash_info
;
278 r
= Trash
<I
>::get(parent
->md_ctx
, parent
->id
, &trash_info
);
279 if (r
== -ENOENT
|| r
== -EOPNOTSUPP
) {
280 parent_image
->trash
= false;
282 lderr(cct
) << "error looking up trash status: " << cpp_strerror(r
)
290 template <typename I
>
291 int Image
<I
>::list_children(I
*ictx
,
292 std::vector
<librbd::linked_image_spec_t
> *images
) {
294 return list_descendants(ictx
, 1, images
);
297 template <typename I
>
298 int Image
<I
>::list_children(I
*ictx
,
299 const cls::rbd::ParentImageSpec
&parent_spec
,
300 std::vector
<librbd::linked_image_spec_t
> *images
) {
302 return list_descendants(ictx
, parent_spec
, 1, images
);
305 template <typename I
>
306 int Image
<I
>::list_descendants(
307 librados::IoCtx
& io_ctx
, const std::string
&image_id
,
308 const std::optional
<size_t> &max_level
,
309 std::vector
<librbd::linked_image_spec_t
> *images
) {
310 ImageCtx
*ictx
= new librbd::ImageCtx("", image_id
, nullptr,
312 int r
= ictx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
317 lderr(ictx
->cct
) << "failed to open descendant " << image_id
318 << " from pool " << io_ctx
.get_pool_name() << ":"
319 << cpp_strerror(r
) << dendl
;
323 r
= list_descendants(ictx
, max_level
, images
);
325 int r1
= ictx
->state
->close();
327 lderr(ictx
->cct
) << "error when closing descendant " << image_id
328 << " from pool " << io_ctx
.get_pool_name() << ":"
329 << cpp_strerror(r
) << dendl
;
335 template <typename I
>
336 int Image
<I
>::list_descendants(
337 I
*ictx
, const std::optional
<size_t> &max_level
,
338 std::vector
<librbd::linked_image_spec_t
> *images
) {
339 std::shared_lock l
{ictx
->image_lock
};
340 std::vector
<librados::snap_t
> snap_ids
;
341 if (ictx
->snap_id
!= CEPH_NOSNAP
) {
342 snap_ids
.push_back(ictx
->snap_id
);
344 snap_ids
= ictx
->snaps
;
346 for (auto snap_id
: snap_ids
) {
347 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
348 ictx
->md_ctx
.get_namespace(),
350 int r
= list_descendants(ictx
, parent_spec
, max_level
, images
);
358 template <typename I
>
359 int Image
<I
>::list_descendants(
360 I
*ictx
, const cls::rbd::ParentImageSpec
&parent_spec
,
361 const std::optional
<size_t> &max_level
,
362 std::vector
<librbd::linked_image_spec_t
> *images
) {
363 auto child_max_level
= max_level
;
364 if (child_max_level
) {
365 if (child_max_level
== 0) {
368 (*child_max_level
)--;
370 CephContext
*cct
= ictx
->cct
;
371 ldout(cct
, 20) << "ictx=" << ictx
<< dendl
;
373 // no children for non-layered or old format image
374 if (!ictx
->test_features(RBD_FEATURE_LAYERING
, ictx
->image_lock
)) {
378 librados::Rados
rados(ictx
->md_ctx
);
380 // search all pools for clone v1 children dependent on this snapshot
381 std::list
<std::pair
<int64_t, std::string
> > pools
;
382 int r
= rados
.pool_list2(pools
);
384 lderr(cct
) << "error listing pools: " << cpp_strerror(r
) << dendl
;
388 for (auto& it
: pools
) {
390 r
= rados
.pool_get_base_tier(it
.first
, &base_tier
);
392 ldout(cct
, 1) << "pool " << it
.second
<< " no longer exists" << dendl
;
395 lderr(cct
) << "error retrieving base tier for pool " << it
.second
399 if (it
.first
!= base_tier
) {
400 // pool is a cache; skip it
405 r
= librbd::util::create_ioctx(
406 ictx
->md_ctx
, "child image", it
.first
, {}, &ioctx
);
413 std::set
<std::string
> image_ids
;
414 r
= cls_client::get_children(&ioctx
, RBD_CHILDREN
, parent_spec
,
416 if (r
< 0 && r
!= -ENOENT
) {
417 lderr(cct
) << "error reading list of children from pool " << it
.second
422 for (auto& image_id
: image_ids
) {
424 it
.first
, "", ictx
->md_ctx
.get_namespace(), image_id
, "", false});
425 r
= list_descendants(ioctx
, image_id
, child_max_level
, images
);
432 // retrieve clone v2 children attached to this snapshot
434 r
= librbd::util::create_ioctx(
435 ictx
->md_ctx
, "parent image",parent_spec
.pool_id
,
436 parent_spec
.pool_namespace
, &parent_io_ctx
);
441 cls::rbd::ChildImageSpecs child_images
;
442 r
= cls_client::children_list(
443 &parent_io_ctx
, librbd::util::header_name(parent_spec
.image_id
),
444 parent_spec
.snap_id
, &child_images
);
445 if (r
< 0 && r
!= -ENOENT
&& r
!= -EOPNOTSUPP
) {
446 lderr(cct
) << "error retrieving children: " << cpp_strerror(r
) << dendl
;
450 for (auto& child_image
: child_images
) {
452 child_image
.pool_id
, "", child_image
.pool_namespace
,
453 child_image
.image_id
, "", false});
454 if (!child_max_level
|| *child_max_level
> 0) {
456 r
= librbd::util::create_ioctx(
457 ictx
->md_ctx
, "child image", child_image
.pool_id
,
458 child_image
.pool_namespace
, &ioctx
);
464 r
= list_descendants(ioctx
, child_image
.image_id
, child_max_level
,
472 // batch lookups by pool + namespace
473 std::sort(images
->begin(), images
->end(), compare_by_pool
);
475 int64_t child_pool_id
= -1;
476 librados::IoCtx child_io_ctx
;
477 std::map
<std::string
, std::pair
<std::string
, bool>> child_image_id_to_info
;
478 for (auto& image
: *images
) {
479 if (child_pool_id
== -1 || child_pool_id
!= image
.pool_id
||
480 child_io_ctx
.get_namespace() != image
.pool_namespace
) {
481 r
= librbd::util::create_ioctx(
482 ictx
->md_ctx
, "child image", image
.pool_id
, image
.pool_namespace
,
485 image
.pool_name
= "";
486 image
.image_name
= "";
491 child_pool_id
= image
.pool_id
;
493 child_image_id_to_info
.clear();
495 std::map
<std::string
, std::string
> image_names_to_ids
;
496 r
= list_images_v2(child_io_ctx
, &image_names_to_ids
);
498 lderr(cct
) << "error listing v2 images: " << cpp_strerror(r
) << dendl
;
502 for (auto& [name
, id
] : image_names_to_ids
) {
503 child_image_id_to_info
.insert({id
, {name
, false}});
506 std::vector
<librbd::trash_image_info_t
> trash_images
;
507 r
= Trash
<I
>::list(child_io_ctx
, trash_images
, false);
508 if (r
< 0 && r
!= -EOPNOTSUPP
) {
509 lderr(cct
) << "error listing trash images: " << cpp_strerror(r
)
514 for (auto& it
: trash_images
) {
515 child_image_id_to_info
.insert({
518 it
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
? false : true}});
522 auto it
= child_image_id_to_info
.find(image
.image_id
);
523 if (it
== child_image_id_to_info
.end()) {
524 lderr(cct
) << "error looking up name for image id "
525 << image
.image_id
<< " in pool "
526 << child_io_ctx
.get_pool_name()
527 << (image
.pool_namespace
.empty() ?
528 "" : "/" + image
.pool_namespace
) << dendl
;
532 image
.pool_name
= child_io_ctx
.get_pool_name();
533 image
.image_name
= it
->second
.first
;
534 image
.trash
= it
->second
.second
;
537 // final sort by pool + image names
538 std::sort(images
->begin(), images
->end(), compare
);
542 template <typename I
>
543 int Image
<I
>::deep_copy(I
*src
, librados::IoCtx
& dest_md_ctx
,
544 const char *destname
, ImageOptions
& opts
,
545 ProgressContext
&prog_ctx
) {
546 CephContext
*cct
= (CephContext
*)dest_md_ctx
.cct();
547 ldout(cct
, 20) << src
->name
548 << (src
->snap_name
.length() ? "@" + src
->snap_name
: "")
549 << " -> " << destname
<< " opts = " << opts
<< dendl
;
554 std::shared_lock image_locker
{src
->image_lock
};
556 if (!src
->migration_info
.empty()) {
557 lderr(cct
) << "cannot deep copy migrating image" << dendl
;
561 features
= src
->features
;
562 src_size
= src
->get_image_size(src
->snap_id
);
565 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
566 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
569 lderr(cct
) << "old format not supported for destination image" << dendl
;
572 uint64_t stripe_unit
= src
->stripe_unit
;
573 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
574 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
576 uint64_t stripe_count
= src
->stripe_count
;
577 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
578 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
580 uint64_t order
= src
->order
;
581 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
582 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
584 if (opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
) != 0) {
585 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
587 if (features
& ~RBD_FEATURES_ALL
) {
588 lderr(cct
) << "librbd does not support requested features" << dendl
;
592 uint64_t flatten
= 0;
593 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
594 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
597 cls::rbd::ParentImageSpec parent_spec
;
599 parent_spec
.pool_id
= -1;
601 std::shared_lock image_locker
{src
->image_lock
};
603 // use oldest snapshot or HEAD for parent spec
604 if (!src
->snap_info
.empty()) {
605 parent_spec
= src
->snap_info
.begin()->second
.parent
.spec
;
607 parent_spec
= src
->parent_md
.spec
;
612 if (parent_spec
.pool_id
== -1) {
613 r
= create(dest_md_ctx
, destname
, "", src_size
, opts
, "", "", false);
615 librados::IoCtx parent_io_ctx
;
616 r
= librbd::util::create_ioctx(
617 src
->md_ctx
, "parent image", parent_spec
.pool_id
,
618 parent_spec
.pool_namespace
, &parent_io_ctx
);
623 ConfigProxy config
{cct
->_conf
};
624 api::Config
<I
>::apply_pool_overrides(dest_md_ctx
, &config
);
627 std::string dest_id
= librbd::util::generate_image_id(dest_md_ctx
);
628 auto *req
= image::CloneRequest
<I
>::create(
629 config
, parent_io_ctx
, parent_spec
.image_id
, "", {}, parent_spec
.snap_id
,
630 dest_md_ctx
, destname
, dest_id
, opts
, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
,
631 "", "", src
->op_work_queue
, &ctx
);
636 lderr(cct
) << "header creation failed" << dendl
;
639 opts
.set(RBD_IMAGE_OPTION_ORDER
, static_cast<uint64_t>(order
));
641 auto dest
= new I(destname
, "", nullptr, dest_md_ctx
, false);
642 r
= dest
->state
->open(0);
644 lderr(cct
) << "failed to read newly created header" << dendl
;
648 C_SaferCond lock_ctx
;
650 std::unique_lock locker
{dest
->owner_lock
};
652 if (dest
->exclusive_lock
== nullptr ||
653 dest
->exclusive_lock
->is_lock_owner()) {
654 lock_ctx
.complete(0);
656 dest
->exclusive_lock
->acquire_lock(&lock_ctx
);
662 lderr(cct
) << "failed to request exclusive lock: " << cpp_strerror(r
)
664 dest
->state
->close();
668 r
= deep_copy(src
, dest
, flatten
> 0, prog_ctx
);
670 int close_r
= dest
->state
->close();
671 if (r
== 0 && close_r
< 0) {
677 template <typename I
>
678 int Image
<I
>::deep_copy(I
*src
, I
*dest
, bool flatten
,
679 ProgressContext
&prog_ctx
) {
680 librados::snap_t snap_id_start
= 0;
681 librados::snap_t snap_id_end
;
683 std::shared_lock image_locker
{src
->image_lock
};
684 snap_id_end
= src
->snap_id
;
687 AsioEngine
asio_engine(src
->md_ctx
);
691 deep_copy::ProgressHandler progress_handler
{&prog_ctx
};
692 auto req
= DeepCopyRequest
<I
>::create(
693 src
, dest
, snap_id_start
, snap_id_end
, 0U, flatten
, boost::none
,
694 asio_engine
.get_work_queue(), &snap_seqs
, &progress_handler
, &cond
);
704 template <typename I
>
705 int Image
<I
>::snap_set(I
*ictx
,
706 const cls::rbd::SnapshotNamespace
&snap_namespace
,
707 const char *snap_name
) {
708 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " snap = "
709 << (snap_name
? snap_name
: "NULL") << dendl
;
711 // ignore return value, since we may be set to a non-existent
712 // snapshot and the user is trying to fix that
713 ictx
->state
->refresh_if_required();
715 uint64_t snap_id
= CEPH_NOSNAP
;
716 std::string
name(snap_name
== nullptr ? "" : snap_name
);
718 std::shared_lock image_locker
{ictx
->image_lock
};
719 snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace
{},
721 if (snap_id
== CEPH_NOSNAP
) {
726 return snap_set(ictx
, snap_id
);
729 template <typename I
>
730 int Image
<I
>::snap_set(I
*ictx
, uint64_t snap_id
) {
731 ldout(ictx
->cct
, 20) << "snap_set " << ictx
<< " "
732 << "snap_id=" << snap_id
<< dendl
;
734 // ignore return value, since we may be set to a non-existent
735 // snapshot and the user is trying to fix that
736 ictx
->state
->refresh_if_required();
739 ictx
->state
->snap_set(snap_id
, &ctx
);
743 lderr(ictx
->cct
) << "failed to " << (snap_id
== CEPH_NOSNAP
? "un" : "")
744 << "set snapshot: " << cpp_strerror(r
) << dendl
;
752 template <typename I
>
753 int Image
<I
>::remove(IoCtx
& io_ctx
, const std::string
&image_name
,
754 ProgressContext
& prog_ctx
)
756 CephContext
*cct((CephContext
*)io_ctx
.cct());
757 ldout(cct
, 20) << "name=" << image_name
<< dendl
;
759 // look up the V2 image id based on the image name
760 std::string image_id
;
761 int r
= cls_client::dir_get_id(&io_ctx
, RBD_DIRECTORY
, image_name
,
764 // check if it already exists in trash from an aborted trash remove attempt
765 std::vector
<trash_image_info_t
> trash_entries
;
766 r
= Trash
<I
>::list(io_ctx
, trash_entries
, false);
770 for (auto& entry
: trash_entries
) {
771 if (entry
.name
== image_name
&&
772 entry
.source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
773 return Trash
<I
>::remove(io_ctx
, entry
.id
, true, prog_ctx
);
778 // fall-through if we failed to locate the image in the V2 directory and
781 lderr(cct
) << "failed to retrieve image id: " << cpp_strerror(r
) << dendl
;
784 // attempt to move the image to the trash (and optionally immediately
786 ConfigProxy
config(cct
->_conf
);
787 Config
<I
>::apply_pool_overrides(io_ctx
, &config
);
789 rbd_trash_image_source_t trash_image_source
=
790 RBD_TRASH_IMAGE_SOURCE_REMOVING
;
791 uint64_t expire_seconds
= 0;
792 if (config
.get_val
<bool>("rbd_move_to_trash_on_remove")) {
793 // keep the image in the trash upon remove requests
794 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER
;
795 expire_seconds
= config
.get_val
<uint64_t>(
796 "rbd_move_to_trash_on_remove_expire_seconds");
798 // attempt to pre-validate the removal before moving to trash and
800 r
= pre_remove_image
<I
>(io_ctx
, image_id
);
802 if (config
.get_val
<bool>("rbd_move_parent_to_trash_on_remove")) {
803 // keep the image in the trash until the last child is removed
804 trash_image_source
= RBD_TRASH_IMAGE_SOURCE_USER_PARENT
;
806 lderr(cct
) << "image has snapshots - not removing" << dendl
;
809 } else if (r
< 0 && r
!= -ENOENT
) {
814 r
= Trash
<I
>::move(io_ctx
, trash_image_source
, image_name
, image_id
,
817 if (trash_image_source
== RBD_TRASH_IMAGE_SOURCE_REMOVING
) {
818 // proceed with attempting to immediately remove the image
819 r
= Trash
<I
>::remove(io_ctx
, image_id
, true, prog_ctx
);
821 if (r
== -ENOTEMPTY
|| r
== -EBUSY
|| r
== -EMLINK
) {
822 // best-effort try to restore the image if the removal
823 // failed for possible expected reasons
824 Trash
<I
>::restore(io_ctx
, {cls::rbd::TRASH_IMAGE_SOURCE_REMOVING
},
825 image_id
, image_name
);
829 } else if (r
< 0 && r
!= -EOPNOTSUPP
) {
833 // fall-through if trash isn't supported
836 AsioEngine
asio_engine(io_ctx
);
838 // might be a V1 image format that cannot be moved to the trash
839 // and would not have been listed in the V2 directory -- or the OSDs
840 // are too old and don't support the trash feature
842 auto req
= librbd::image::RemoveRequest
<I
>::create(
843 io_ctx
, image_name
, "", false, false, prog_ctx
,
844 asio_engine
.get_work_queue(), &cond
);
850 template <typename I
>
851 int Image
<I
>::flatten_children(I
*ictx
, const char* snap_name
,
852 ProgressContext
& pctx
) {
853 CephContext
*cct
= ictx
->cct
;
854 ldout(cct
, 20) << "children flatten " << ictx
->name
<< dendl
;
856 int r
= ictx
->state
->refresh_if_required();
861 std::shared_lock l
{ictx
->image_lock
};
862 snap_t snap_id
= ictx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
865 cls::rbd::ParentImageSpec parent_spec
{ictx
->md_ctx
.get_id(),
866 ictx
->md_ctx
.get_namespace(),
868 std::vector
<librbd::linked_image_spec_t
> child_images
;
869 r
= list_children(ictx
, parent_spec
, &child_images
);
874 size_t size
= child_images
.size();
879 librados::IoCtx child_io_ctx
;
880 int64_t child_pool_id
= -1;
882 for (auto &child_image
: child_images
){
883 std::string pool
= child_image
.pool_name
;
884 if (child_pool_id
== -1 ||
885 child_pool_id
!= child_image
.pool_id
||
886 child_io_ctx
.get_namespace() != child_image
.pool_namespace
) {
887 r
= librbd::util::create_ioctx(
888 ictx
->md_ctx
, "child image", child_image
.pool_id
,
889 child_image
.pool_namespace
, &child_io_ctx
);
894 child_pool_id
= child_image
.pool_id
;
897 ImageCtx
*imctx
= new ImageCtx("", child_image
.image_id
, nullptr,
898 child_io_ctx
, false);
899 r
= imctx
->state
->open(0);
901 lderr(cct
) << "error opening image: " << cpp_strerror(r
) << dendl
;
905 if ((imctx
->features
& RBD_FEATURE_DEEP_FLATTEN
) == 0 &&
906 !imctx
->snaps
.empty()) {
907 lderr(cct
) << "snapshot in-use by " << pool
<< "/" << imctx
->name
909 imctx
->state
->close();
913 librbd::NoOpProgressContext prog_ctx
;
914 r
= imctx
->operations
->flatten(prog_ctx
);
916 lderr(cct
) << "error flattening image: " << pool
<< "/"
917 << (child_image
.pool_namespace
.empty() ?
918 "" : "/" + child_image
.pool_namespace
)
919 << child_image
.image_name
<< cpp_strerror(r
) << dendl
;
920 imctx
->state
->close();
924 r
= imctx
->state
->close();
926 lderr(cct
) << "failed to close image: " << cpp_strerror(r
) << dendl
;
930 pctx
.update_progress(++i
, size
);
931 ceph_assert(i
<= size
);
937 template <typename I
>
938 int Image
<I
>::encryption_format(I
* ictx
, encryption_format_t format
,
939 encryption_options_t opts
, size_t opts_size
,
941 if (ictx
->parent
!= nullptr) {
942 lderr(ictx
->cct
) << "cannot format a cloned image" << dendl
;
946 crypto::EncryptionFormat
<I
>* result_format
;
947 auto r
= util::create_encryption_format(
948 ictx
->cct
, format
, opts
, opts_size
, c_api
, &result_format
);
954 auto req
= librbd::crypto::FormatRequest
<I
>::create(
955 ictx
, std::unique_ptr
<crypto::EncryptionFormat
<I
>>(result_format
),
961 template <typename I
>
962 int Image
<I
>::encryption_load(I
* ictx
, encryption_format_t format
,
963 encryption_options_t opts
, size_t opts_size
,
965 crypto::EncryptionFormat
<I
>* result_format
;
966 auto r
= util::create_encryption_format(
967 ictx
->cct
, format
, opts
, opts_size
, c_api
, &result_format
);
973 auto req
= librbd::crypto::LoadRequest
<I
>::create(
974 ictx
, std::unique_ptr
<crypto::EncryptionFormat
<I
>>(result_format
),
981 } // namespace librbd
983 template class librbd::api::Image
<librbd::ImageCtx
>;