]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/api/Image.cc
7df0f4fc69e2e85e04a40e46131ed5abdb0b6ed5
[ceph.git] / ceph / src / librbd / api / Image.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
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>
30
31 #define dout_subsys ceph_subsys_rbd
32 #undef dout_prefix
33 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
34
35 using librados::snap_t;
36
37 namespace librbd {
38 namespace api {
39
40 namespace {
41
42 bool compare_by_pool(const librbd::linked_image_spec_t& lhs,
43 const librbd::linked_image_spec_t& rhs)
44 {
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;
49 }
50 return false;
51 }
52
53 bool compare(const librbd::linked_image_spec_t& lhs,
54 const librbd::linked_image_spec_t& rhs)
55 {
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;
66 }
67 return false;
68 }
69
70 template <typename I>
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);
74 if (r < 0) {
75 return r;
76 }
77
78 C_SaferCond ctx;
79 auto req = image::PreRemoveRequest<I>::create(image_ctx, false, &ctx);
80 req->send();
81
82 r = ctx.wait();
83 image_ctx->state->close();
84 return r;
85 }
86
87 } // anonymous namespace
88
89 template <typename I>
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();
93 }
94
95 int64_t pool_id;
96 int r = cls_client::get_data_pool(&ictx->md_ctx, ictx->header_oid, &pool_id);
97 if (r < 0) {
98 CephContext *cct = ictx->cct;
99 lderr(cct) << "error getting data pool ID: " << cpp_strerror(r) << dendl;
100 return r;
101 }
102
103 return pool_id;
104 }
105
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;
110
111 int r = ictx->state->refresh_if_required();
112 if (r < 0) {
113 return r;
114 }
115
116 std::shared_lock image_locker{ictx->image_lock};
117 *op_features = ictx->op_features;
118 return 0;
119 }
120
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;
126
127 int r;
128 images->clear();
129
130 if (io_ctx.get_namespace().empty()) {
131 bufferlist bl;
132 r = io_ctx.read(RBD_DIRECTORY, bl, 0, 0);
133 if (r == -ENOENT) {
134 return 0;
135 } else if (r < 0) {
136 lderr(cct) << "error listing v1 images: " << cpp_strerror(r) << dendl;
137 return r;
138 }
139
140 // V1 format images are in a tmap
141 if (bl.length()) {
142 auto p = bl.cbegin();
143 bufferlist header;
144 std::map<std::string, bufferlist> m;
145 decode(header, p);
146 decode(m, p);
147 for (auto& it : m) {
148 images->push_back({.id ="", .name = it.first});
149 }
150 }
151 }
152
153 // V2 format images
154 std::map<std::string, std::string> image_names_to_ids;
155 r = list_images_v2(io_ctx, &image_names_to_ids);
156 if (r < 0) {
157 lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl;
158 return r;
159 }
160
161 for (const auto& img_pair : image_names_to_ids) {
162 images->push_back({.id = img_pair.second,
163 .name = img_pair.first});
164 }
165
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;
171 return r;
172 }
173
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});
178
179 }
180 }
181
182 return 0;
183 }
184
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;
189
190 // new format images are accessed by class methods
191 int r;
192 int max_read = 1024;
193 string last_read = "";
194 do {
195 map<string, string> images_page;
196 r = cls_client::dir_list(&io_ctx, RBD_DIRECTORY, last_read, max_read,
197 &images_page);
198 if (r < 0 && r != -ENOENT) {
199 lderr(cct) << "error listing image in directory: "
200 << cpp_strerror(r) << dendl;
201 return r;
202 } else if (r == -ENOENT) {
203 break;
204 }
205 for (map<string, string>::const_iterator it = images_page.begin();
206 it != images_page.end(); ++it) {
207 images->insert(*it);
208 }
209 if (!images_page.empty()) {
210 last_read = images_page.rbegin()->first;
211 }
212 r = images_page.size();
213 } while (r == max_read);
214
215 return 0;
216 }
217
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;
224
225 int r = ictx->state->refresh_if_required();
226 if (r < 0) {
227 return r;
228 }
229
230 std::shared_lock image_locker{ictx->image_lock};
231
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();
236 }
237 };
238
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();
245
246 parent = ictx->parent->parent;
247 }
248
249 if (parent == nullptr) {
250 return -ENOENT;
251 }
252
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();
256
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)
264 << dendl;
265 return -ENOENT;
266 }
267
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;
271 }
272
273 parent_image->image_id = parent->id;
274 parent_image->image_name = parent->name;
275 parent_image->trash = true;
276
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;
281 } else if (r < 0) {
282 lderr(cct) << "error looking up trash status: " << cpp_strerror(r)
283 << dendl;
284 return r;
285 }
286
287 return 0;
288 }
289
290 template <typename I>
291 int Image<I>::list_children(I *ictx,
292 std::vector<librbd::linked_image_spec_t> *images) {
293 images->clear();
294 return list_descendants(ictx, 1, images);
295 }
296
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) {
301 images->clear();
302 return list_descendants(ictx, parent_spec, 1, images);
303 }
304
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,
311 io_ctx, true);
312 int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
313 if (r < 0) {
314 if (r == -ENOENT) {
315 return 0;
316 }
317 lderr(ictx->cct) << "failed to open descendant " << image_id
318 << " from pool " << io_ctx.get_pool_name() << ":"
319 << cpp_strerror(r) << dendl;
320 return r;
321 }
322
323 r = list_descendants(ictx, max_level, images);
324
325 int r1 = ictx->state->close();
326 if (r1 < 0) {
327 lderr(ictx->cct) << "error when closing descendant " << image_id
328 << " from pool " << io_ctx.get_pool_name() << ":"
329 << cpp_strerror(r) << dendl;
330 }
331
332 return r;
333 }
334
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);
343 } else {
344 snap_ids = ictx->snaps;
345 }
346 for (auto snap_id : snap_ids) {
347 cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
348 ictx->md_ctx.get_namespace(),
349 ictx->id, snap_id};
350 int r = list_descendants(ictx, parent_spec, max_level, images);
351 if (r < 0) {
352 return r;
353 }
354 }
355 return 0;
356 }
357
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) {
366 return 0;
367 }
368 (*child_max_level)--;
369 }
370 CephContext *cct = ictx->cct;
371 ldout(cct, 20) << "ictx=" << ictx << dendl;
372
373 // no children for non-layered or old format image
374 if (!ictx->test_features(RBD_FEATURE_LAYERING, ictx->image_lock)) {
375 return 0;
376 }
377
378 librados::Rados rados(ictx->md_ctx);
379
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);
383 if (r < 0) {
384 lderr(cct) << "error listing pools: " << cpp_strerror(r) << dendl;
385 return r;
386 }
387
388 for (auto& it : pools) {
389 int64_t base_tier;
390 r = rados.pool_get_base_tier(it.first, &base_tier);
391 if (r == -ENOENT) {
392 ldout(cct, 1) << "pool " << it.second << " no longer exists" << dendl;
393 continue;
394 } else if (r < 0) {
395 lderr(cct) << "error retrieving base tier for pool " << it.second
396 << dendl;
397 return r;
398 }
399 if (it.first != base_tier) {
400 // pool is a cache; skip it
401 continue;
402 }
403
404 IoCtx ioctx;
405 r = librbd::util::create_ioctx(
406 ictx->md_ctx, "child image", it.first, {}, &ioctx);
407 if (r == -ENOENT) {
408 continue;
409 } else if (r < 0) {
410 return r;
411 }
412
413 std::set<std::string> image_ids;
414 r = cls_client::get_children(&ioctx, RBD_CHILDREN, parent_spec,
415 image_ids);
416 if (r < 0 && r != -ENOENT) {
417 lderr(cct) << "error reading list of children from pool " << it.second
418 << dendl;
419 return r;
420 }
421
422 for (auto& image_id : image_ids) {
423 images->push_back({
424 it.first, "", ictx->md_ctx.get_namespace(), image_id, "", false});
425 r = list_descendants(ioctx, image_id, child_max_level, images);
426 if (r < 0) {
427 return r;
428 }
429 }
430 }
431
432 // retrieve clone v2 children attached to this snapshot
433 IoCtx parent_io_ctx;
434 r = librbd::util::create_ioctx(
435 ictx->md_ctx, "parent image",parent_spec.pool_id,
436 parent_spec.pool_namespace, &parent_io_ctx);
437 if (r < 0) {
438 return r;
439 }
440
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;
447 return r;
448 }
449
450 for (auto& child_image : child_images) {
451 images->push_back({
452 child_image.pool_id, "", child_image.pool_namespace,
453 child_image.image_id, "", false});
454 if (!child_max_level || *child_max_level > 0) {
455 IoCtx ioctx;
456 r = librbd::util::create_ioctx(
457 ictx->md_ctx, "child image", child_image.pool_id,
458 child_image.pool_namespace, &ioctx);
459 if (r == -ENOENT) {
460 continue;
461 } else if (r < 0) {
462 return r;
463 }
464 r = list_descendants(ioctx, child_image.image_id, child_max_level,
465 images);
466 if (r < 0) {
467 return r;
468 }
469 }
470 }
471
472 // batch lookups by pool + namespace
473 std::sort(images->begin(), images->end(), compare_by_pool);
474
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,
483 &child_io_ctx);
484 if (r == -ENOENT) {
485 image.pool_name = "";
486 image.image_name = "";
487 continue;
488 } else if (r < 0) {
489 return r;
490 }
491 child_pool_id = image.pool_id;
492
493 child_image_id_to_info.clear();
494
495 std::map<std::string, std::string> image_names_to_ids;
496 r = list_images_v2(child_io_ctx, &image_names_to_ids);
497 if (r < 0) {
498 lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl;
499 return r;
500 }
501
502 for (auto& [name, id] : image_names_to_ids) {
503 child_image_id_to_info.insert({id, {name, false}});
504 }
505
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)
510 << dendl;
511 return r;
512 }
513
514 for (auto& it : trash_images) {
515 child_image_id_to_info.insert({
516 it.id,
517 {it.name,
518 it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING ? false : true}});
519 }
520 }
521
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;
529 return -ENOENT;
530 }
531
532 image.pool_name = child_io_ctx.get_pool_name();
533 image.image_name = it->second.first;
534 image.trash = it->second.second;
535 }
536
537 // final sort by pool + image names
538 std::sort(images->begin(), images->end(), compare);
539 return 0;
540 }
541
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;
550
551 uint64_t features;
552 uint64_t src_size;
553 {
554 std::shared_lock image_locker{src->image_lock};
555
556 if (!src->migration_info.empty()) {
557 lderr(cct) << "cannot deep copy migrating image" << dendl;
558 return -EBUSY;
559 }
560
561 features = src->features;
562 src_size = src->get_image_size(src->snap_id);
563 }
564 uint64_t format = 2;
565 if (opts.get(RBD_IMAGE_OPTION_FORMAT, &format) != 0) {
566 opts.set(RBD_IMAGE_OPTION_FORMAT, format);
567 }
568 if (format == 1) {
569 lderr(cct) << "old format not supported for destination image" << dendl;
570 return -EINVAL;
571 }
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);
575 }
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);
579 }
580 uint64_t order = src->order;
581 if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
582 opts.set(RBD_IMAGE_OPTION_ORDER, order);
583 }
584 if (opts.get(RBD_IMAGE_OPTION_FEATURES, &features) != 0) {
585 opts.set(RBD_IMAGE_OPTION_FEATURES, features);
586 }
587 if (features & ~RBD_FEATURES_ALL) {
588 lderr(cct) << "librbd does not support requested features" << dendl;
589 return -ENOSYS;
590 }
591
592 uint64_t flatten = 0;
593 if (opts.get(RBD_IMAGE_OPTION_FLATTEN, &flatten) == 0) {
594 opts.unset(RBD_IMAGE_OPTION_FLATTEN);
595 }
596
597 cls::rbd::ParentImageSpec parent_spec;
598 if (flatten > 0) {
599 parent_spec.pool_id = -1;
600 } else {
601 std::shared_lock image_locker{src->image_lock};
602
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;
606 } else {
607 parent_spec = src->parent_md.spec;
608 }
609 }
610
611 int r;
612 if (parent_spec.pool_id == -1) {
613 r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false);
614 } else {
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);
619 if (r < 0) {
620 return r;
621 }
622
623 ConfigProxy config{cct->_conf};
624 api::Config<I>::apply_pool_overrides(dest_md_ctx, &config);
625
626 C_SaferCond ctx;
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);
632 req->send();
633 r = ctx.wait();
634 }
635 if (r < 0) {
636 lderr(cct) << "header creation failed" << dendl;
637 return r;
638 }
639 opts.set(RBD_IMAGE_OPTION_ORDER, static_cast<uint64_t>(order));
640
641 auto dest = new I(destname, "", nullptr, dest_md_ctx, false);
642 r = dest->state->open(0);
643 if (r < 0) {
644 lderr(cct) << "failed to read newly created header" << dendl;
645 return r;
646 }
647
648 C_SaferCond lock_ctx;
649 {
650 std::unique_lock locker{dest->owner_lock};
651
652 if (dest->exclusive_lock == nullptr ||
653 dest->exclusive_lock->is_lock_owner()) {
654 lock_ctx.complete(0);
655 } else {
656 dest->exclusive_lock->acquire_lock(&lock_ctx);
657 }
658 }
659
660 r = lock_ctx.wait();
661 if (r < 0) {
662 lderr(cct) << "failed to request exclusive lock: " << cpp_strerror(r)
663 << dendl;
664 dest->state->close();
665 return r;
666 }
667
668 r = deep_copy(src, dest, flatten > 0, prog_ctx);
669
670 int close_r = dest->state->close();
671 if (r == 0 && close_r < 0) {
672 r = close_r;
673 }
674 return r;
675 }
676
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;
682 {
683 std::shared_lock image_locker{src->image_lock};
684 snap_id_end = src->snap_id;
685 }
686
687 AsioEngine asio_engine(src->md_ctx);
688
689 C_SaferCond cond;
690 SnapSeqs snap_seqs;
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);
695 req->send();
696 int r = cond.wait();
697 if (r < 0) {
698 return r;
699 }
700
701 return 0;
702 }
703
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;
710
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();
714
715 uint64_t snap_id = CEPH_NOSNAP;
716 std::string name(snap_name == nullptr ? "" : snap_name);
717 if (!name.empty()) {
718 std::shared_lock image_locker{ictx->image_lock};
719 snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace{},
720 snap_name);
721 if (snap_id == CEPH_NOSNAP) {
722 return -ENOENT;
723 }
724 }
725
726 return snap_set(ictx, snap_id);
727 }
728
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;
733
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();
737
738 C_SaferCond ctx;
739 ictx->state->snap_set(snap_id, &ctx);
740 int r = ctx.wait();
741 if (r < 0) {
742 if (r != -ENOENT) {
743 lderr(ictx->cct) << "failed to " << (snap_id == CEPH_NOSNAP ? "un" : "")
744 << "set snapshot: " << cpp_strerror(r) << dendl;
745 }
746 return r;
747 }
748
749 return 0;
750 }
751
752 template <typename I>
753 int Image<I>::remove(IoCtx& io_ctx, const std::string &image_name,
754 ProgressContext& prog_ctx)
755 {
756 CephContext *cct((CephContext *)io_ctx.cct());
757 ldout(cct, 20) << "name=" << image_name << dendl;
758
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,
762 &image_id);
763 if (r == -ENOENT) {
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);
767 if (r < 0) {
768 return r;
769 } else if (r >= 0) {
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);
774 }
775 }
776 }
777
778 // fall-through if we failed to locate the image in the V2 directory and
779 // trash
780 } else if (r < 0) {
781 lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
782 return r;
783 } else {
784 // attempt to move the image to the trash (and optionally immediately
785 // delete the image)
786 ConfigProxy config(cct->_conf);
787 Config<I>::apply_pool_overrides(io_ctx, &config);
788
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");
797 } else {
798 // attempt to pre-validate the removal before moving to trash and
799 // removing
800 r = pre_remove_image<I>(io_ctx, image_id);
801 if (r == -ECHILD) {
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;
805 } else {
806 lderr(cct) << "image has snapshots - not removing" << dendl;
807 return -ENOTEMPTY;
808 }
809 } else if (r < 0 && r != -ENOENT) {
810 return r;
811 }
812 }
813
814 r = Trash<I>::move(io_ctx, trash_image_source, image_name, image_id,
815 expire_seconds);
816 if (r >= 0) {
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);
820
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);
826 }
827 }
828 return r;
829 } else if (r < 0 && r != -EOPNOTSUPP) {
830 return r;
831 }
832
833 // fall-through if trash isn't supported
834 }
835
836 AsioEngine asio_engine(io_ctx);
837
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
841 C_SaferCond cond;
842 auto req = librbd::image::RemoveRequest<I>::create(
843 io_ctx, image_name, "", false, false, prog_ctx,
844 asio_engine.get_work_queue(), &cond);
845 req->send();
846
847 return cond.wait();
848 }
849
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;
855
856 int r = ictx->state->refresh_if_required();
857 if (r < 0) {
858 return r;
859 }
860
861 std::shared_lock l{ictx->image_lock};
862 snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
863 snap_name);
864
865 cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
866 ictx->md_ctx.get_namespace(),
867 ictx->id, snap_id};
868 std::vector<librbd::linked_image_spec_t> child_images;
869 r = list_children(ictx, parent_spec, &child_images);
870 if (r < 0) {
871 return r;
872 }
873
874 size_t size = child_images.size();
875 if (size == 0) {
876 return 0;
877 }
878
879 librados::IoCtx child_io_ctx;
880 int64_t child_pool_id = -1;
881 size_t i = 0;
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);
890 if (r < 0) {
891 return r;
892 }
893
894 child_pool_id = child_image.pool_id;
895 }
896
897 ImageCtx *imctx = new ImageCtx("", child_image.image_id, nullptr,
898 child_io_ctx, false);
899 r = imctx->state->open(0);
900 if (r < 0) {
901 lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl;
902 return r;
903 }
904
905 if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 &&
906 !imctx->snaps.empty()) {
907 lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name
908 << dendl;
909 imctx->state->close();
910 return -EBUSY;
911 }
912
913 librbd::NoOpProgressContext prog_ctx;
914 r = imctx->operations->flatten(prog_ctx);
915 if (r < 0) {
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();
921 return r;
922 }
923
924 r = imctx->state->close();
925 if (r < 0) {
926 lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl;
927 return r;
928 }
929
930 pctx.update_progress(++i, size);
931 ceph_assert(i <= size);
932 }
933
934 return 0;
935 }
936
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,
940 bool c_api) {
941 if (ictx->parent != nullptr) {
942 lderr(ictx->cct) << "cannot format a cloned image" << dendl;
943 return -ENOTSUP;
944 }
945
946 crypto::EncryptionFormat<I>* result_format;
947 auto r = util::create_encryption_format(
948 ictx->cct, format, opts, opts_size, c_api, &result_format);
949 if (r != 0) {
950 return r;
951 }
952
953 C_SaferCond cond;
954 auto req = librbd::crypto::FormatRequest<I>::create(
955 ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
956 &cond);
957 req->send();
958 return cond.wait();
959 }
960
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,
964 bool c_api) {
965 crypto::EncryptionFormat<I>* result_format;
966 auto r = util::create_encryption_format(
967 ictx->cct, format, opts, opts_size, c_api, &result_format);
968 if (r != 0) {
969 return r;
970 }
971
972 C_SaferCond cond;
973 auto req = librbd::crypto::LoadRequest<I>::create(
974 ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
975 &cond);
976 req->send();
977 return cond.wait();
978 }
979
980 } // namespace api
981 } // namespace librbd
982
983 template class librbd::api::Image<librbd::ImageCtx>;