]> git.proxmox.com Git - ceph.git/blob - ceph/src/librbd/api/Image.cc
import ceph quincy 17.2.6
[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 "librbd/io/AioCompletion.h"
30 #include "librbd/io/ImageDispatchSpec.h"
31 #include <boost/scope_exit.hpp>
32
33 #define dout_subsys ceph_subsys_rbd
34 #undef dout_prefix
35 #define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
36
37 using std::map;
38 using std::string;
39 using librados::snap_t;
40
41 namespace librbd {
42 namespace api {
43
44 namespace {
45
46 bool compare_by_pool(const librbd::linked_image_spec_t& lhs,
47 const librbd::linked_image_spec_t& rhs)
48 {
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;
53 }
54 return false;
55 }
56
57 bool compare(const librbd::linked_image_spec_t& lhs,
58 const librbd::linked_image_spec_t& rhs)
59 {
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;
70 }
71 return false;
72 }
73
74 template <typename I>
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);
78 if (r < 0) {
79 return r;
80 }
81
82 C_SaferCond ctx;
83 auto req = image::PreRemoveRequest<I>::create(image_ctx, false, &ctx);
84 req->send();
85
86 r = ctx.wait();
87 image_ctx->state->close();
88 return r;
89 }
90
91 } // anonymous namespace
92
93 template <typename I>
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();
97 }
98
99 int64_t pool_id;
100 int r = cls_client::get_data_pool(&ictx->md_ctx, ictx->header_oid, &pool_id);
101 if (r < 0) {
102 CephContext *cct = ictx->cct;
103 lderr(cct) << "error getting data pool ID: " << cpp_strerror(r) << dendl;
104 return r;
105 }
106
107 return pool_id;
108 }
109
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;
114
115 int r = ictx->state->refresh_if_required();
116 if (r < 0) {
117 return r;
118 }
119
120 std::shared_lock image_locker{ictx->image_lock};
121 *op_features = ictx->op_features;
122 return 0;
123 }
124
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;
130
131 int r;
132 images->clear();
133
134 if (io_ctx.get_namespace().empty()) {
135 bufferlist bl;
136 r = io_ctx.read(RBD_DIRECTORY, bl, 0, 0);
137 if (r == -ENOENT) {
138 return 0;
139 } else if (r < 0) {
140 lderr(cct) << "error listing v1 images: " << cpp_strerror(r) << dendl;
141 return r;
142 }
143
144 // V1 format images are in a tmap
145 if (bl.length()) {
146 auto p = bl.cbegin();
147 bufferlist header;
148 std::map<std::string, bufferlist> m;
149 decode(header, p);
150 decode(m, p);
151 for (auto& it : m) {
152 images->push_back({.id ="", .name = it.first});
153 }
154 }
155 }
156
157 // V2 format images
158 std::map<std::string, std::string> image_names_to_ids;
159 r = list_images_v2(io_ctx, &image_names_to_ids);
160 if (r < 0) {
161 lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl;
162 return r;
163 }
164
165 for (const auto& img_pair : image_names_to_ids) {
166 images->push_back({.id = img_pair.second,
167 .name = img_pair.first});
168 }
169
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;
175 return r;
176 }
177
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});
182
183 }
184 }
185
186 return 0;
187 }
188
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;
193
194 // new format images are accessed by class methods
195 int r;
196 int max_read = 1024;
197 string last_read = "";
198 do {
199 map<string, string> images_page;
200 r = cls_client::dir_list(&io_ctx, RBD_DIRECTORY, last_read, max_read,
201 &images_page);
202 if (r < 0 && r != -ENOENT) {
203 lderr(cct) << "error listing image in directory: "
204 << cpp_strerror(r) << dendl;
205 return r;
206 } else if (r == -ENOENT) {
207 break;
208 }
209 for (map<string, string>::const_iterator it = images_page.begin();
210 it != images_page.end(); ++it) {
211 images->insert(*it);
212 }
213 if (!images_page.empty()) {
214 last_read = images_page.rbegin()->first;
215 }
216 r = images_page.size();
217 } while (r == max_read);
218
219 return 0;
220 }
221
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;
228
229 int r = ictx->state->refresh_if_required();
230 if (r < 0) {
231 return r;
232 }
233
234 std::shared_lock image_locker{ictx->image_lock};
235
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();
240 }
241 };
242
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();
249
250 parent = ictx->parent->parent;
251 }
252
253 if (parent == nullptr) {
254 return -ENOENT;
255 }
256
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();
260
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)
268 << dendl;
269 return -ENOENT;
270 }
271
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;
275 }
276
277 parent_image->image_id = parent->id;
278 parent_image->image_name = parent->name;
279 parent_image->trash = true;
280
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;
285 } else if (r < 0) {
286 lderr(cct) << "error looking up trash status: " << cpp_strerror(r)
287 << dendl;
288 return r;
289 }
290
291 return 0;
292 }
293
294 template <typename I>
295 int Image<I>::list_children(I *ictx,
296 std::vector<librbd::linked_image_spec_t> *images) {
297 images->clear();
298 return list_descendants(ictx, 1, images);
299 }
300
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) {
305 images->clear();
306 return list_descendants(ictx, parent_spec, 1, images);
307 }
308
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,
315 io_ctx, true);
316 CephContext *cct = ictx->cct;
317 int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
318 if (r < 0) {
319 if (r == -ENOENT) {
320 return 0;
321 }
322 lderr(cct) << "failed to open descendant " << image_id
323 << " from pool " << io_ctx.get_pool_name() << ":"
324 << cpp_strerror(r) << dendl;
325 return r;
326 }
327
328 r = list_descendants(ictx, max_level, images);
329
330 int r1 = ictx->state->close();
331 if (r1 < 0) {
332 lderr(cct) << "error when closing descendant " << image_id
333 << " from pool " << io_ctx.get_pool_name() << ":"
334 << cpp_strerror(r1) << dendl;
335 }
336
337 return r;
338 }
339
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);
348 } else {
349 snap_ids = ictx->snaps;
350 }
351 for (auto snap_id : snap_ids) {
352 cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
353 ictx->md_ctx.get_namespace(),
354 ictx->id, snap_id};
355 int r = list_descendants(ictx, parent_spec, max_level, images);
356 if (r < 0) {
357 return r;
358 }
359 }
360 return 0;
361 }
362
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) {
371 return 0;
372 }
373 (*child_max_level)--;
374 }
375 CephContext *cct = ictx->cct;
376 ldout(cct, 20) << "ictx=" << ictx << dendl;
377
378 // no children for non-layered or old format image
379 if (!ictx->test_features(RBD_FEATURE_LAYERING, ictx->image_lock)) {
380 return 0;
381 }
382
383 librados::Rados rados(ictx->md_ctx);
384
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);
388 if (r < 0) {
389 lderr(cct) << "error listing pools: " << cpp_strerror(r) << dendl;
390 return r;
391 }
392
393 for (auto& it : pools) {
394 int64_t base_tier;
395 r = rados.pool_get_base_tier(it.first, &base_tier);
396 if (r == -ENOENT) {
397 ldout(cct, 1) << "pool " << it.second << " no longer exists" << dendl;
398 continue;
399 } else if (r < 0) {
400 lderr(cct) << "error retrieving base tier for pool " << it.second
401 << dendl;
402 return r;
403 }
404 if (it.first != base_tier) {
405 // pool is a cache; skip it
406 continue;
407 }
408
409 IoCtx ioctx;
410 r = librbd::util::create_ioctx(
411 ictx->md_ctx, "child image", it.first, {}, &ioctx);
412 if (r == -ENOENT) {
413 continue;
414 } else if (r < 0) {
415 return r;
416 }
417
418 std::set<std::string> image_ids;
419 r = cls_client::get_children(&ioctx, RBD_CHILDREN, parent_spec,
420 image_ids);
421 if (r < 0 && r != -ENOENT) {
422 lderr(cct) << "error reading list of children from pool " << it.second
423 << dendl;
424 return r;
425 }
426
427 for (auto& image_id : image_ids) {
428 images->push_back({
429 it.first, "", ictx->md_ctx.get_namespace(), image_id, "", false});
430 r = list_descendants(ioctx, image_id, child_max_level, images);
431 if (r < 0) {
432 return r;
433 }
434 }
435 }
436
437 // retrieve clone v2 children attached to this snapshot
438 IoCtx parent_io_ctx;
439 r = librbd::util::create_ioctx(
440 ictx->md_ctx, "parent image",parent_spec.pool_id,
441 parent_spec.pool_namespace, &parent_io_ctx);
442 if (r < 0) {
443 return r;
444 }
445
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;
452 return r;
453 }
454
455 for (auto& child_image : child_images) {
456 images->push_back({
457 child_image.pool_id, "", child_image.pool_namespace,
458 child_image.image_id, "", false});
459 if (!child_max_level || *child_max_level > 0) {
460 IoCtx ioctx;
461 r = librbd::util::create_ioctx(
462 ictx->md_ctx, "child image", child_image.pool_id,
463 child_image.pool_namespace, &ioctx);
464 if (r == -ENOENT) {
465 continue;
466 } else if (r < 0) {
467 return r;
468 }
469 r = list_descendants(ioctx, child_image.image_id, child_max_level,
470 images);
471 if (r < 0) {
472 return r;
473 }
474 }
475 }
476
477 // batch lookups by pool + namespace
478 std::sort(images->begin(), images->end(), compare_by_pool);
479
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,
488 &child_io_ctx);
489 if (r == -ENOENT) {
490 image.pool_name = "";
491 image.image_name = "";
492 continue;
493 } else if (r < 0) {
494 return r;
495 }
496 child_pool_id = image.pool_id;
497
498 child_image_id_to_info.clear();
499
500 std::map<std::string, std::string> image_names_to_ids;
501 r = list_images_v2(child_io_ctx, &image_names_to_ids);
502 if (r < 0) {
503 lderr(cct) << "error listing v2 images: " << cpp_strerror(r) << dendl;
504 return r;
505 }
506
507 for (auto& [name, id] : image_names_to_ids) {
508 child_image_id_to_info.insert({id, {name, false}});
509 }
510
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)
515 << dendl;
516 return r;
517 }
518
519 for (auto& it : trash_images) {
520 child_image_id_to_info.insert({
521 it.id,
522 {it.name,
523 it.source == RBD_TRASH_IMAGE_SOURCE_REMOVING ? false : true}});
524 }
525 }
526
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;
534 return -ENOENT;
535 }
536
537 image.pool_name = child_io_ctx.get_pool_name();
538 image.image_name = it->second.first;
539 image.trash = it->second.second;
540 }
541
542 // final sort by pool + image names
543 std::sort(images->begin(), images->end(), compare);
544 return 0;
545 }
546
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;
555
556 uint64_t features;
557 uint64_t src_size;
558 {
559 std::shared_lock image_locker{src->image_lock};
560
561 if (!src->migration_info.empty()) {
562 lderr(cct) << "cannot deep copy migrating image" << dendl;
563 return -EBUSY;
564 }
565
566 features = src->features;
567 src_size = src->get_image_size(src->snap_id);
568 }
569 uint64_t format = 2;
570 if (opts.get(RBD_IMAGE_OPTION_FORMAT, &format) != 0) {
571 opts.set(RBD_IMAGE_OPTION_FORMAT, format);
572 }
573 if (format == 1) {
574 lderr(cct) << "old format not supported for destination image" << dendl;
575 return -EINVAL;
576 }
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);
580 }
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);
584 }
585 uint64_t order = src->order;
586 if (opts.get(RBD_IMAGE_OPTION_ORDER, &order) != 0) {
587 opts.set(RBD_IMAGE_OPTION_ORDER, order);
588 }
589 if (opts.get(RBD_IMAGE_OPTION_FEATURES, &features) != 0) {
590 opts.set(RBD_IMAGE_OPTION_FEATURES, features);
591 }
592 if (features & ~RBD_FEATURES_ALL) {
593 lderr(cct) << "librbd does not support requested features" << dendl;
594 return -ENOSYS;
595 }
596
597 uint64_t flatten = 0;
598 if (opts.get(RBD_IMAGE_OPTION_FLATTEN, &flatten) == 0) {
599 opts.unset(RBD_IMAGE_OPTION_FLATTEN);
600 }
601
602 cls::rbd::ParentImageSpec parent_spec;
603 if (flatten > 0) {
604 parent_spec.pool_id = -1;
605 } else {
606 std::shared_lock image_locker{src->image_lock};
607
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;
611 } else {
612 parent_spec = src->parent_md.spec;
613 }
614 }
615
616 int r;
617 if (parent_spec.pool_id == -1) {
618 r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false);
619 } else {
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);
624 if (r < 0) {
625 return r;
626 }
627
628 ConfigProxy config{cct->_conf};
629 api::Config<I>::apply_pool_overrides(dest_md_ctx, &config);
630
631 C_SaferCond ctx;
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);
637 req->send();
638 r = ctx.wait();
639 }
640 if (r < 0) {
641 lderr(cct) << "header creation failed" << dendl;
642 return r;
643 }
644 opts.set(RBD_IMAGE_OPTION_ORDER, static_cast<uint64_t>(order));
645
646 auto dest = new I(destname, "", nullptr, dest_md_ctx, false);
647 r = dest->state->open(0);
648 if (r < 0) {
649 lderr(cct) << "failed to read newly created header" << dendl;
650 return r;
651 }
652
653 C_SaferCond lock_ctx;
654 {
655 std::unique_lock locker{dest->owner_lock};
656
657 if (dest->exclusive_lock == nullptr ||
658 dest->exclusive_lock->is_lock_owner()) {
659 lock_ctx.complete(0);
660 } else {
661 dest->exclusive_lock->acquire_lock(&lock_ctx);
662 }
663 }
664
665 r = lock_ctx.wait();
666 if (r < 0) {
667 lderr(cct) << "failed to request exclusive lock: " << cpp_strerror(r)
668 << dendl;
669 dest->state->close();
670 return r;
671 }
672
673 r = deep_copy(src, dest, flatten > 0, prog_ctx);
674
675 int close_r = dest->state->close();
676 if (r == 0 && close_r < 0) {
677 r = close_r;
678 }
679 return r;
680 }
681
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;
687 {
688 std::shared_lock owner_locker{src->owner_lock};
689 auto aio_comp = io::AioCompletion::create_and_start(&flush_ctx, src,
690 io::AIO_TYPE_FLUSH);
691 auto req = io::ImageDispatchSpec::create_flush(
692 *src, io::IMAGE_DISPATCH_LAYER_INTERNAL_START,
693 aio_comp, io::FLUSH_SOURCE_INTERNAL, {});
694 req->send();
695 }
696 int r = flush_ctx.wait();
697 if (r < 0) {
698 return r;
699 }
700
701 librados::snap_t snap_id_start = 0;
702 librados::snap_t snap_id_end;
703 {
704 std::shared_lock image_locker{src->image_lock};
705 snap_id_end = src->snap_id;
706 }
707
708 AsioEngine asio_engine(src->md_ctx);
709
710 C_SaferCond cond;
711 SnapSeqs snap_seqs;
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);
716 req->send();
717 r = cond.wait();
718 if (r < 0) {
719 return r;
720 }
721
722 return 0;
723 }
724
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;
731
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();
735
736 uint64_t snap_id = CEPH_NOSNAP;
737 std::string name(snap_name == nullptr ? "" : snap_name);
738 if (!name.empty()) {
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) {
742 return -ENOENT;
743 }
744 }
745
746 return snap_set(ictx, snap_id);
747 }
748
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;
753
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();
757
758 C_SaferCond ctx;
759 ictx->state->snap_set(snap_id, &ctx);
760 int r = ctx.wait();
761 if (r < 0) {
762 if (r != -ENOENT) {
763 lderr(ictx->cct) << "failed to " << (snap_id == CEPH_NOSNAP ? "un" : "")
764 << "set snapshot: " << cpp_strerror(r) << dendl;
765 }
766 return r;
767 }
768
769 return 0;
770 }
771
772 template <typename I>
773 int Image<I>::remove(IoCtx& io_ctx, const std::string &image_name,
774 ProgressContext& prog_ctx)
775 {
776 CephContext *cct((CephContext *)io_ctx.cct());
777 ldout(cct, 20) << "name=" << image_name << dendl;
778
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,
782 &image_id);
783 if (r == -ENOENT) {
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);
787 if (r < 0) {
788 return r;
789 }
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);
795 if (r < 0) {
796 lderr(cct) << "error getting image id " << entry.id
797 << " info from trash: " << cpp_strerror(r) << dendl;
798 return r;
799 }
800 if (spec.state == cls::rbd::TRASH_IMAGE_STATE_MOVING) {
801 r = Trash<I>::move(io_ctx, entry.source, entry.name, entry.id, 0);
802 if (r < 0) {
803 return r;
804 }
805 }
806 return Trash<I>::remove(io_ctx, entry.id, true, prog_ctx);
807 }
808 }
809
810 // fall-through if we failed to locate the image in the V2 directory and
811 // trash
812 } else if (r < 0) {
813 lderr(cct) << "failed to retrieve image id: " << cpp_strerror(r) << dendl;
814 return r;
815 } else {
816 // attempt to move the image to the trash (and optionally immediately
817 // delete the image)
818 ConfigProxy config(cct->_conf);
819 Config<I>::apply_pool_overrides(io_ctx, &config);
820
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");
829 } else {
830 // attempt to pre-validate the removal before moving to trash and
831 // removing
832 r = pre_remove_image<I>(io_ctx, image_id);
833 if (r == -ECHILD) {
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;
837 } else {
838 lderr(cct) << "image has snapshots - not removing" << dendl;
839 return -ENOTEMPTY;
840 }
841 } else if (r < 0 && r != -ENOENT) {
842 return r;
843 }
844 }
845
846 r = Trash<I>::move(io_ctx, trash_image_source, image_name, image_id,
847 expire_seconds);
848 if (r >= 0) {
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);
852
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);
858 }
859 }
860 return r;
861 } else if (r < 0 && r != -EOPNOTSUPP) {
862 return r;
863 }
864
865 // fall-through if trash isn't supported
866 }
867
868 AsioEngine asio_engine(io_ctx);
869
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
873 C_SaferCond cond;
874 auto req = librbd::image::RemoveRequest<I>::create(
875 io_ctx, image_name, "", false, false, prog_ctx,
876 asio_engine.get_work_queue(), &cond);
877 req->send();
878
879 return cond.wait();
880 }
881
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;
887
888 int r = ictx->state->refresh_if_required();
889 if (r < 0) {
890 return r;
891 }
892
893 std::shared_lock l{ictx->image_lock};
894 snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
895 snap_name);
896
897 cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
898 ictx->md_ctx.get_namespace(),
899 ictx->id, snap_id};
900 std::vector<librbd::linked_image_spec_t> child_images;
901 r = list_children(ictx, parent_spec, &child_images);
902 if (r < 0) {
903 return r;
904 }
905
906 size_t size = child_images.size();
907 if (size == 0) {
908 return 0;
909 }
910
911 librados::IoCtx child_io_ctx;
912 int64_t child_pool_id = -1;
913 size_t i = 0;
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);
922 if (r < 0) {
923 return r;
924 }
925
926 child_pool_id = child_image.pool_id;
927 }
928
929 ImageCtx *imctx = new ImageCtx("", child_image.image_id, nullptr,
930 child_io_ctx, false);
931 r = imctx->state->open(0);
932 if (r < 0) {
933 lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl;
934 return r;
935 }
936
937 if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 &&
938 !imctx->snaps.empty()) {
939 lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name
940 << dendl;
941 imctx->state->close();
942 return -EBUSY;
943 }
944
945 librbd::NoOpProgressContext prog_ctx;
946 r = imctx->operations->flatten(prog_ctx);
947 if (r < 0) {
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();
953 return r;
954 }
955
956 r = imctx->state->close();
957 if (r < 0) {
958 lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl;
959 return r;
960 }
961
962 pctx.update_progress(++i, size);
963 ceph_assert(i <= size);
964 }
965
966 return 0;
967 }
968
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,
972 bool c_api) {
973 if (ictx->parent != nullptr) {
974 lderr(ictx->cct) << "cannot format a cloned image" << dendl;
975 return -ENOTSUP;
976 }
977
978 crypto::EncryptionFormat<I>* result_format;
979 auto r = util::create_encryption_format(
980 ictx->cct, format, opts, opts_size, c_api, &result_format);
981 if (r != 0) {
982 return r;
983 }
984
985 C_SaferCond cond;
986 auto req = librbd::crypto::FormatRequest<I>::create(
987 ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
988 &cond);
989 req->send();
990 return cond.wait();
991 }
992
993 template <typename I>
994 int Image<I>::encryption_load(I* ictx, encryption_format_t format,
995 encryption_options_t opts, size_t opts_size,
996 bool c_api) {
997 crypto::EncryptionFormat<I>* result_format;
998 auto r = util::create_encryption_format(
999 ictx->cct, format, opts, opts_size, c_api, &result_format);
1000 if (r != 0) {
1001 return r;
1002 }
1003
1004 C_SaferCond cond;
1005 auto req = librbd::crypto::LoadRequest<I>::create(
1006 ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
1007 &cond);
1008 req->send();
1009 return cond.wait();
1010 }
1011
1012 } // namespace api
1013 } // namespace librbd
1014
1015 template class librbd::api::Image<librbd::ImageCtx>;