]> git.proxmox.com Git - ceph.git/blob - ceph/src/cls/rbd/cls_rbd.cc
6c22256fd0d9774e0c82d277b56dcdb9b454010e
[ceph.git] / ceph / src / cls / rbd / cls_rbd.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 /** \file
5 *
6 * This is an OSD class that implements methods for
7 * use with rbd.
8 *
9 * Most of these deal with the rbd header object. Methods prefixed
10 * with old_ deal with the original rbd design, in which clients read
11 * and interpreted the header object directly.
12 *
13 * The new format is meant to be opaque to clients - all their
14 * interactions with non-data objects should go through this
15 * class. The OSD class interface leaves the class to implement its
16 * own argument and payload serialization/deserialization, so for ease
17 * of implementation we use the existing ceph encoding/decoding
18 * methods. Something like json might be preferable, but the rbd
19 * kernel module has to be able to understand format as well. The
20 * datatypes exposed to the clients are strings, unsigned integers,
21 * and vectors of those types. The on-wire format can be found in
22 * src/include/encoding.h.
23 *
24 * The methods for interacting with the new format document their
25 * parameters as the client sees them - it would be silly to mention
26 * in each one that they take an input and an output bufferlist.
27 */
28 #include "include/types.h"
29
30 #include <algorithm>
31 #include <errno.h>
32 #include <sstream>
33
34 #include "include/uuid.h"
35 #include "common/bit_vector.hpp"
36 #include "common/errno.h"
37 #include "objclass/objclass.h"
38 #include "osd/osd_types.h"
39 #include "include/rbd_types.h"
40 #include "include/rbd/object_map_types.h"
41
42 #include "cls/rbd/cls_rbd.h"
43 #include "cls/rbd/cls_rbd_types.h"
44
45 #include <boost/algorithm/string/predicate.hpp>
46
47 using std::istringstream;
48 using std::ostringstream;
49 using std::map;
50 using std::set;
51 using std::string;
52 using std::vector;
53
54 using ceph::BitVector;
55 using ceph::bufferlist;
56 using ceph::bufferptr;
57 using ceph::encode;
58 using ceph::decode;
59
60 /*
61 * Object keys:
62 *
63 * <partial list>
64 *
65 * stripe_unit: size in bytes of the stripe unit. if not present,
66 * the stripe unit is assumed to match the object size (1 << order).
67 *
68 * stripe_count: number of objects to stripe over before looping back.
69 * if not present or 1, striping is disabled. this is the default.
70 *
71 */
72
73 CLS_VER(2,0)
74 CLS_NAME(rbd)
75
76 #define RBD_MAX_KEYS_READ 64
77 #define RBD_SNAP_KEY_PREFIX "snapshot_"
78 #define RBD_SNAP_CHILDREN_KEY_PREFIX "snap_children_"
79 #define RBD_DIR_ID_KEY_PREFIX "id_"
80 #define RBD_DIR_NAME_KEY_PREFIX "name_"
81 #define RBD_METADATA_KEY_PREFIX "metadata_"
82
83 namespace {
84
85 uint64_t get_encode_features(cls_method_context_t hctx) {
86 uint64_t features = 0;
87 ceph_release_t require_osd_release = cls_get_required_osd_release(hctx);
88 if (require_osd_release >= ceph_release_t::nautilus) {
89 features |= CEPH_FEATURE_SERVER_NAUTILUS;
90 }
91 return features;
92 }
93
94 bool calc_sparse_extent(const bufferptr &bp, size_t sparse_size,
95 uint64_t length, size_t *write_offset,
96 size_t *write_length, size_t *offset) {
97 size_t extent_size;
98 if (*offset + sparse_size > length) {
99 extent_size = length - *offset;
100 } else {
101 extent_size = sparse_size;
102 }
103
104 bufferptr extent(bp, *offset, extent_size);
105 *offset += extent_size;
106
107 bool extent_is_zero = extent.is_zero();
108 if (!extent_is_zero) {
109 *write_length += extent_size;
110 }
111 if (extent_is_zero && *write_length == 0) {
112 *write_offset += extent_size;
113 }
114
115 if ((extent_is_zero || *offset == length) && *write_length != 0) {
116 return true;
117 }
118 return false;
119 }
120
121 } // anonymous namespace
122
123 static int snap_read_header(cls_method_context_t hctx, bufferlist& bl)
124 {
125 unsigned snap_count = 0;
126 uint64_t snap_names_len = 0;
127 struct rbd_obj_header_ondisk *header;
128
129 CLS_LOG(20, "snapshots_list");
130
131 while (1) {
132 int len = sizeof(*header) +
133 snap_count * sizeof(struct rbd_obj_snap_ondisk) +
134 snap_names_len;
135
136 int rc = cls_cxx_read(hctx, 0, len, &bl);
137 if (rc < 0)
138 return rc;
139
140 if (bl.length() < sizeof(*header))
141 return -EINVAL;
142
143 header = (struct rbd_obj_header_ondisk *)bl.c_str();
144 ceph_assert(header);
145
146 if ((snap_count != header->snap_count) ||
147 (snap_names_len != header->snap_names_len)) {
148 snap_count = header->snap_count;
149 snap_names_len = header->snap_names_len;
150 bl.clear();
151 continue;
152 }
153 break;
154 }
155
156 return 0;
157 }
158
159 static void key_from_snap_id(snapid_t snap_id, string *out)
160 {
161 ostringstream oss;
162 oss << RBD_SNAP_KEY_PREFIX
163 << std::setw(16) << std::setfill('0') << std::hex << snap_id;
164 *out = oss.str();
165 }
166
167 static snapid_t snap_id_from_key(const string &key) {
168 istringstream iss(key);
169 uint64_t id;
170 iss.ignore(strlen(RBD_SNAP_KEY_PREFIX)) >> std::hex >> id;
171 return id;
172 }
173
174 template<typename T>
175 static int read_key(cls_method_context_t hctx, const string &key, T *out)
176 {
177 bufferlist bl;
178 int r = cls_cxx_map_get_val(hctx, key, &bl);
179 if (r < 0) {
180 if (r != -ENOENT) {
181 CLS_ERR("error reading omap key %s: %s", key.c_str(), cpp_strerror(r).c_str());
182 }
183 return r;
184 }
185
186 try {
187 auto it = bl.cbegin();
188 decode(*out, it);
189 } catch (const ceph::buffer::error &err) {
190 CLS_ERR("error decoding %s", key.c_str());
191 return -EIO;
192 }
193
194 return 0;
195 }
196
197 template <typename T>
198 static int write_key(cls_method_context_t hctx, const string &key, const T &t) {
199 bufferlist bl;
200 encode(t, bl);
201
202 int r = cls_cxx_map_set_val(hctx, key, &bl);
203 if (r < 0) {
204 CLS_ERR("failed to set omap key: %s", key.c_str());
205 return r;
206 }
207 return 0;
208 }
209
210 template <typename T>
211 static int write_key(cls_method_context_t hctx, const string &key, const T &t,
212 uint64_t features) {
213 bufferlist bl;
214 encode(t, bl, features);
215
216 int r = cls_cxx_map_set_val(hctx, key, &bl);
217 if (r < 0) {
218 CLS_ERR("failed to set omap key: %s", key.c_str());
219 return r;
220 }
221 return 0;
222 }
223
224 static int remove_key(cls_method_context_t hctx, const string &key) {
225 int r = cls_cxx_map_remove_key(hctx, key);
226 if (r < 0 && r != -ENOENT) {
227 CLS_ERR("failed to remove key: %s", key.c_str());
228 return r;
229 }
230 return 0;
231 }
232
233 static bool is_valid_id(const string &id) {
234 if (!id.size())
235 return false;
236 for (size_t i = 0; i < id.size(); ++i) {
237 if (!isalnum(id[i])) {
238 return false;
239 }
240 }
241 return true;
242 }
243
244 /**
245 * verify that the header object exists
246 *
247 * @return 0 if the object exists, -ENOENT if it does not, or other error
248 */
249 static int check_exists(cls_method_context_t hctx)
250 {
251 uint64_t size;
252 time_t mtime;
253 return cls_cxx_stat(hctx, &size, &mtime);
254 }
255
256 namespace image {
257
258 /**
259 * check that given feature(s) are set
260 *
261 * @param hctx context
262 * @param need features needed
263 * @return 0 if features are set, negative error (like ENOEXEC) otherwise
264 */
265 int require_feature(cls_method_context_t hctx, uint64_t need)
266 {
267 uint64_t features;
268 int r = read_key(hctx, "features", &features);
269 if (r == -ENOENT) // this implies it's an old-style image with no features
270 return -ENOEXEC;
271 if (r < 0)
272 return r;
273 if ((features & need) != need) {
274 CLS_LOG(10, "require_feature missing feature %llx, have %llx",
275 (unsigned long long)need, (unsigned long long)features);
276 return -ENOEXEC;
277 }
278 return 0;
279 }
280
281 std::string snap_children_key_from_snap_id(snapid_t snap_id)
282 {
283 ostringstream oss;
284 oss << RBD_SNAP_CHILDREN_KEY_PREFIX
285 << std::setw(16) << std::setfill('0') << std::hex << snap_id;
286 return oss.str();
287 }
288
289 int set_op_features(cls_method_context_t hctx, uint64_t op_features,
290 uint64_t mask) {
291 uint64_t orig_features;
292 int r = read_key(hctx, "features", &orig_features);
293 if (r < 0) {
294 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
295 return r;
296 }
297
298 uint64_t orig_op_features = 0;
299 r = read_key(hctx, "op_features", &orig_op_features);
300 if (r < 0 && r != -ENOENT) {
301 CLS_ERR("Could not read op features off disk: %s", cpp_strerror(r).c_str());
302 return r;
303 }
304
305 op_features = (orig_op_features & ~mask) | (op_features & mask);
306 CLS_LOG(10, "op_features=%" PRIu64 " orig_op_features=%" PRIu64,
307 op_features, orig_op_features);
308 if (op_features == orig_op_features) {
309 return 0;
310 }
311
312 uint64_t features = orig_features;
313 if (op_features == 0ULL) {
314 features &= ~RBD_FEATURE_OPERATIONS;
315
316 r = cls_cxx_map_remove_key(hctx, "op_features");
317 if (r == -ENOENT) {
318 r = 0;
319 }
320 } else {
321 features |= RBD_FEATURE_OPERATIONS;
322
323 bufferlist bl;
324 encode(op_features, bl);
325 r = cls_cxx_map_set_val(hctx, "op_features", &bl);
326 }
327
328 if (r < 0) {
329 CLS_ERR("error updating op features: %s", cpp_strerror(r).c_str());
330 return r;
331 }
332
333 if (features != orig_features) {
334 bufferlist bl;
335 encode(features, bl);
336 r = cls_cxx_map_set_val(hctx, "features", &bl);
337 if (r < 0) {
338 CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
339 return r;
340 }
341 }
342
343 return 0;
344 }
345
346 int set_migration(cls_method_context_t hctx,
347 const cls::rbd::MigrationSpec &migration_spec, bool init) {
348 if (init) {
349 bufferlist bl;
350 int r = cls_cxx_map_get_val(hctx, "migration", &bl);
351 if (r != -ENOENT) {
352 if (r == 0) {
353 CLS_LOG(10, "migration already set");
354 return -EEXIST;
355 }
356 CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
357 return r;
358 }
359
360 uint64_t features = 0;
361 r = read_key(hctx, "features", &features);
362 if (r == -ENOENT) {
363 CLS_LOG(20, "no features, assuming v1 format");
364 bufferlist header;
365 r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
366 if (r < 0) {
367 CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
368 return r;
369 }
370 if (header.length() != sizeof(RBD_HEADER_TEXT)) {
371 CLS_ERR("unrecognized v1 header format");
372 return -ENXIO;
373 }
374 if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) != 0) {
375 if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(),
376 header.length()) == 0) {
377 CLS_LOG(10, "migration already set");
378 return -EEXIST;
379 } else {
380 CLS_ERR("unrecognized v1 header format");
381 return -ENXIO;
382 }
383 }
384 if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
385 CLS_LOG(10, "v1 format image can only be migration source");
386 return -EINVAL;
387 }
388
389 header.clear();
390 header.append(RBD_MIGRATE_HEADER_TEXT);
391 r = cls_cxx_write(hctx, 0, header.length(), &header);
392 if (r < 0) {
393 CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
394 return r;
395 }
396 } else if (r < 0) {
397 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
398 return r;
399 } else if ((features & RBD_FEATURE_MIGRATING) != 0ULL) {
400 if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST) {
401 CLS_LOG(10, "migrating feature already set");
402 return -EEXIST;
403 }
404 } else {
405 features |= RBD_FEATURE_MIGRATING;
406 bl.clear();
407 encode(features, bl);
408 r = cls_cxx_map_set_val(hctx, "features", &bl);
409 if (r < 0) {
410 CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
411 return r;
412 }
413 }
414 }
415
416 bufferlist bl;
417 encode(migration_spec, bl);
418 int r = cls_cxx_map_set_val(hctx, "migration", &bl);
419 if (r < 0) {
420 CLS_ERR("error setting migration: %s", cpp_strerror(r).c_str());
421 return r;
422 }
423
424 return 0;
425 }
426
427 int read_migration(cls_method_context_t hctx,
428 cls::rbd::MigrationSpec *migration_spec) {
429 uint64_t features = 0;
430 int r = read_key(hctx, "features", &features);
431 if (r == -ENOENT) {
432 CLS_LOG(20, "no features, assuming v1 format");
433 bufferlist header;
434 r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
435 if (r < 0) {
436 CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
437 return r;
438 }
439 if (header.length() != sizeof(RBD_HEADER_TEXT)) {
440 CLS_ERR("unrecognized v1 header format");
441 return -ENXIO;
442 }
443 if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
444 if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
445 CLS_LOG(10, "migration feature not set");
446 return -EINVAL;
447 } else {
448 CLS_ERR("unrecognized v1 header format");
449 return -ENXIO;
450 }
451 }
452 if (migration_spec->header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
453 CLS_LOG(10, "v1 format image can only be migration source");
454 return -EINVAL;
455 }
456 } else if (r < 0) {
457 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
458 return r;
459 } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
460 CLS_LOG(10, "migration feature not set");
461 return -EINVAL;
462 }
463
464 r = read_key(hctx, "migration", migration_spec);
465 if (r < 0) {
466 CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
467 return r;
468 }
469
470 return 0;
471 }
472
473 int remove_migration(cls_method_context_t hctx) {
474 int r = remove_key(hctx, "migration");
475 if (r < 0) {
476 return r;
477 }
478
479 uint64_t features = 0;
480 r = read_key(hctx, "features", &features);
481 if (r == -ENOENT) {
482 CLS_LOG(20, "no features, assuming v1 format");
483 bufferlist header;
484 r = cls_cxx_read(hctx, 0, sizeof(RBD_MIGRATE_HEADER_TEXT), &header);
485 if (header.length() != sizeof(RBD_MIGRATE_HEADER_TEXT)) {
486 CLS_ERR("unrecognized v1 header format");
487 return -ENXIO;
488 }
489 if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
490 if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
491 CLS_LOG(10, "migration feature not set");
492 return -EINVAL;
493 } else {
494 CLS_ERR("unrecognized v1 header format");
495 return -ENXIO;
496 }
497 }
498 header.clear();
499 header.append(RBD_HEADER_TEXT);
500 r = cls_cxx_write(hctx, 0, header.length(), &header);
501 if (r < 0) {
502 CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
503 return r;
504 }
505 } else if (r < 0) {
506 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
507 return r;
508 } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
509 CLS_LOG(10, "migrating feature not set");
510 } else {
511 features &= ~RBD_FEATURE_MIGRATING;
512 bufferlist bl;
513 encode(features, bl);
514 r = cls_cxx_map_set_val(hctx, "features", &bl);
515 if (r < 0) {
516 CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
517 return r;
518 }
519 }
520
521 return 0;
522 }
523
524 namespace snapshot {
525
526 template<typename L>
527 int iterate(cls_method_context_t hctx, L& lambda) {
528 int max_read = RBD_MAX_KEYS_READ;
529 string last_read = RBD_SNAP_KEY_PREFIX;
530 bool more = false;
531 do {
532 map<string, bufferlist> vals;
533 int r = cls_cxx_map_get_vals(hctx, last_read, RBD_SNAP_KEY_PREFIX,
534 max_read, &vals, &more);
535 if (r < 0) {
536 return r;
537 }
538
539 cls_rbd_snap snap_meta;
540 for (auto& val : vals) {
541 auto iter = val.second.cbegin();
542 try {
543 decode(snap_meta, iter);
544 } catch (const ceph::buffer::error &err) {
545 CLS_ERR("error decoding snapshot metadata for snap : %s",
546 val.first.c_str());
547 return -EIO;
548 }
549
550 r = lambda(snap_meta);
551 if (r < 0) {
552 return r;
553 }
554 }
555
556 if (!vals.empty()) {
557 last_read = vals.rbegin()->first;
558 }
559 } while (more);
560
561 return 0;
562 }
563
564 int write(cls_method_context_t hctx, const std::string& snap_key,
565 cls_rbd_snap&& snap) {
566 int r;
567 uint64_t encode_features = get_encode_features(hctx);
568 if (snap.migrate_parent_format(encode_features)) {
569 // ensure the normalized parent link exists before removing it from the
570 // snapshot record
571 cls_rbd_parent on_disk_parent;
572 r = read_key(hctx, "parent", &on_disk_parent);
573 if (r < 0 && r != -ENOENT) {
574 return r;
575 }
576
577 if (!on_disk_parent.exists()) {
578 on_disk_parent = snap.parent;
579 on_disk_parent.head_overlap = std::nullopt;
580
581 r = write_key(hctx, "parent", on_disk_parent, encode_features);
582 if (r < 0) {
583 return r;
584 }
585 }
586
587 // only store the parent overlap in the snapshot
588 snap.parent_overlap = snap.parent.head_overlap;
589 snap.parent = {};
590 }
591
592 r = write_key(hctx, snap_key, snap, encode_features);
593 if (r < 0) {
594 return r;
595 }
596 return 0;
597 }
598
599 } // namespace snapshot
600
601 namespace parent {
602
603 int attach(cls_method_context_t hctx, cls_rbd_parent parent,
604 bool reattach) {
605 int r = check_exists(hctx);
606 if (r < 0) {
607 CLS_LOG(20, "cls_rbd::image::parent::attach: child doesn't exist");
608 return r;
609 }
610
611 r = image::require_feature(hctx, RBD_FEATURE_LAYERING);
612 if (r < 0) {
613 CLS_LOG(20, "cls_rbd::image::parent::attach: child does not support "
614 "layering");
615 return r;
616 }
617
618 CLS_LOG(20, "cls_rbd::image::parent::attach: pool=%" PRIi64 ", ns=%s, id=%s, "
619 "snapid=%" PRIu64 ", size=%" PRIu64,
620 parent.pool_id, parent.pool_namespace.c_str(),
621 parent.image_id.c_str(), parent.snap_id.val,
622 parent.head_overlap.value_or(0ULL));
623 if (!parent.exists() || parent.head_overlap.value_or(0ULL) == 0ULL) {
624 return -EINVAL;
625 }
626
627 // make sure there isn't already a parent
628 cls_rbd_parent on_disk_parent;
629 r = read_key(hctx, "parent", &on_disk_parent);
630 if (r < 0 && r != -ENOENT) {
631 return r;
632 }
633
634 auto on_disk_parent_without_overlap{on_disk_parent};
635 on_disk_parent_without_overlap.head_overlap = parent.head_overlap;
636
637 if (r == 0 &&
638 (on_disk_parent.head_overlap ||
639 on_disk_parent_without_overlap != parent) &&
640 !reattach) {
641 CLS_LOG(20, "cls_rbd::parent::attach: existing legacy parent "
642 "pool=%" PRIi64 ", ns=%s, id=%s, snapid=%" PRIu64 ", "
643 "overlap=%" PRIu64,
644 on_disk_parent.pool_id, on_disk_parent.pool_namespace.c_str(),
645 on_disk_parent.image_id.c_str(), on_disk_parent.snap_id.val,
646 on_disk_parent.head_overlap.value_or(0ULL));
647 return -EEXIST;
648 }
649
650 // our overlap is the min of our size and the parent's size.
651 uint64_t our_size;
652 r = read_key(hctx, "size", &our_size);
653 if (r < 0) {
654 return r;
655 }
656
657 parent.head_overlap = std::min(*parent.head_overlap, our_size);
658
659 r = write_key(hctx, "parent", parent, get_encode_features(hctx));
660 if (r < 0) {
661 return r;
662 }
663
664 return 0;
665 }
666
667 int detach(cls_method_context_t hctx, bool legacy_api) {
668 int r = check_exists(hctx);
669 if (r < 0) {
670 CLS_LOG(20, "cls_rbd::parent::detach: child doesn't exist");
671 return r;
672 }
673
674 uint64_t features;
675 r = read_key(hctx, "features", &features);
676 if (r == -ENOENT || ((features & RBD_FEATURE_LAYERING) == 0)) {
677 CLS_LOG(20, "cls_rbd::image::parent::detach: child does not support "
678 "layering");
679 return -ENOEXEC;
680 } else if (r < 0) {
681 return r;
682 }
683
684 cls_rbd_parent on_disk_parent;
685 r = read_key(hctx, "parent", &on_disk_parent);
686 if (r < 0) {
687 return r;
688 } else if (legacy_api && !on_disk_parent.pool_namespace.empty()) {
689 return -EXDEV;
690 } else if (!on_disk_parent.head_overlap) {
691 return -ENOENT;
692 }
693
694 auto detach_lambda = [hctx, features](const cls_rbd_snap& snap_meta) {
695 if (snap_meta.parent.pool_id != -1 || snap_meta.parent_overlap) {
696 if ((features & RBD_FEATURE_DEEP_FLATTEN) != 0ULL) {
697 // remove parent reference from snapshot
698 cls_rbd_snap snap_meta_copy = snap_meta;
699 snap_meta_copy.parent = {};
700 snap_meta_copy.parent_overlap = std::nullopt;
701
702 std::string snap_key;
703 key_from_snap_id(snap_meta_copy.id, &snap_key);
704 int r = snapshot::write(hctx, snap_key, std::move(snap_meta_copy));
705 if (r < 0) {
706 return r;
707 }
708 } else {
709 return -EEXIST;
710 }
711 }
712 return 0;
713 };
714
715 r = snapshot::iterate(hctx, detach_lambda);
716 bool has_child_snaps = (r == -EEXIST);
717 if (r < 0 && r != -EEXIST) {
718 return r;
719 }
720
721 ceph_release_t require_osd_release = cls_get_required_osd_release(hctx);
722 if (has_child_snaps && require_osd_release >= ceph_release_t::nautilus) {
723 // remove overlap from HEAD revision but keep spec for snapshots
724 on_disk_parent.head_overlap = std::nullopt;
725 r = write_key(hctx, "parent", on_disk_parent, get_encode_features(hctx));
726 if (r < 0) {
727 return r;
728 }
729 } else {
730 r = remove_key(hctx, "parent");
731 if (r < 0 && r != -ENOENT) {
732 return r;
733 }
734 }
735
736 if (!has_child_snaps) {
737 // disable clone child op feature if no longer associated
738 r = set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_CHILD);
739 if (r < 0) {
740 return r;
741 }
742 }
743 return 0;
744 }
745
746 } // namespace parent
747 } // namespace image
748
749 /**
750 * Initialize the header with basic metadata.
751 * Extra features may initialize more fields in the future.
752 * Everything is stored as key/value pairs as omaps in the header object.
753 *
754 * If features the OSD does not understand are requested, -ENOSYS is
755 * returned.
756 *
757 * Input:
758 * @param size number of bytes in the image (uint64_t)
759 * @param order bits to shift to determine the size of data objects (uint8_t)
760 * @param features what optional things this image will use (uint64_t)
761 * @param object_prefix a prefix for all the data objects
762 * @param data_pool_id pool id where data objects is stored (int64_t)
763 *
764 * Output:
765 * @return 0 on success, negative error code on failure
766 */
767 int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
768 {
769 string object_prefix;
770 uint64_t features, size;
771 uint8_t order;
772 int64_t data_pool_id = -1;
773
774 try {
775 auto iter = in->cbegin();
776 decode(size, iter);
777 decode(order, iter);
778 decode(features, iter);
779 decode(object_prefix, iter);
780 if (!iter.end()) {
781 decode(data_pool_id, iter);
782 }
783 } catch (const ceph::buffer::error &err) {
784 return -EINVAL;
785 }
786
787 CLS_LOG(20, "create object_prefix=%s size=%llu order=%u features=%llu",
788 object_prefix.c_str(), (unsigned long long)size, order,
789 (unsigned long long)features);
790
791 if (features & ~RBD_FEATURES_ALL) {
792 return -ENOSYS;
793 }
794
795 if (!object_prefix.size()) {
796 return -EINVAL;
797 }
798
799 bufferlist stored_prefixbl;
800 int r = cls_cxx_map_get_val(hctx, "object_prefix", &stored_prefixbl);
801 if (r != -ENOENT) {
802 CLS_ERR("reading object_prefix returned %d", r);
803 return -EEXIST;
804 }
805
806 bufferlist sizebl;
807 bufferlist orderbl;
808 bufferlist featuresbl;
809 bufferlist object_prefixbl;
810 bufferlist snap_seqbl;
811 bufferlist timestampbl;
812 uint64_t snap_seq = 0;
813 utime_t timestamp = ceph_clock_now();
814 encode(size, sizebl);
815 encode(order, orderbl);
816 encode(features, featuresbl);
817 encode(object_prefix, object_prefixbl);
818 encode(snap_seq, snap_seqbl);
819 encode(timestamp, timestampbl);
820
821 map<string, bufferlist> omap_vals;
822 omap_vals["size"] = sizebl;
823 omap_vals["order"] = orderbl;
824 omap_vals["features"] = featuresbl;
825 omap_vals["object_prefix"] = object_prefixbl;
826 omap_vals["snap_seq"] = snap_seqbl;
827 omap_vals["create_timestamp"] = timestampbl;
828 omap_vals["access_timestamp"] = timestampbl;
829 omap_vals["modify_timestamp"] = timestampbl;
830
831 if ((features & RBD_FEATURE_OPERATIONS) != 0ULL) {
832 CLS_ERR("Attempting to set internal feature: operations");
833 return -EINVAL;
834 }
835
836 if (features & RBD_FEATURE_DATA_POOL) {
837 if (data_pool_id == -1) {
838 CLS_ERR("data pool not provided with feature enabled");
839 return -EINVAL;
840 }
841
842 bufferlist data_pool_id_bl;
843 encode(data_pool_id, data_pool_id_bl);
844 omap_vals["data_pool_id"] = data_pool_id_bl;
845 } else if (data_pool_id != -1) {
846 CLS_ERR("data pool provided with feature disabled");
847 return -EINVAL;
848 }
849
850 r = cls_cxx_map_set_vals(hctx, &omap_vals);
851 if (r < 0)
852 return r;
853
854 return 0;
855 }
856
857 /**
858 * Input:
859 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t) (deprecated)
860 * @param read_only true if the image will be used read-only (bool)
861 *
862 * Output:
863 * @param features list of enabled features for the given snapshot (uint64_t)
864 * @param incompatible incompatible feature bits
865 * @returns 0 on success, negative error code on failure
866 */
867 int get_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
868 {
869 bool read_only = false;
870
871 auto iter = in->cbegin();
872 try {
873 uint64_t snap_id;
874 decode(snap_id, iter);
875 if (!iter.end()) {
876 decode(read_only, iter);
877 }
878 } catch (const ceph::buffer::error &err) {
879 return -EINVAL;
880 }
881
882 CLS_LOG(20, "get_features read_only=%d", read_only);
883
884 uint64_t features;
885 int r = read_key(hctx, "features", &features);
886 if (r < 0) {
887 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
888 return r;
889 }
890
891 uint64_t incompatible = (read_only ? features & RBD_FEATURES_INCOMPATIBLE :
892 features & RBD_FEATURES_RW_INCOMPATIBLE);
893 encode(features, *out);
894 encode(incompatible, *out);
895 return 0;
896 }
897
898 /**
899 * set the image features
900 *
901 * Input:
902 * @param features image features
903 * @param mask image feature mask
904 *
905 * Output:
906 * none
907 *
908 * @returns 0 on success, negative error code upon failure
909 */
910 int set_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
911 {
912 uint64_t features;
913 uint64_t mask;
914 auto iter = in->cbegin();
915 try {
916 decode(features, iter);
917 decode(mask, iter);
918 } catch (const ceph::buffer::error &err) {
919 return -EINVAL;
920 }
921
922 // check that features exists to make sure this is a header object
923 // that was created correctly
924 uint64_t orig_features = 0;
925 int r = read_key(hctx, "features", &orig_features);
926 if (r < 0 && r != -ENOENT) {
927 CLS_ERR("Could not read image's features off disk: %s",
928 cpp_strerror(r).c_str());
929 return r;
930 }
931
932 if ((mask & RBD_FEATURES_INTERNAL) != 0ULL) {
933 CLS_ERR("Attempting to set internal feature: %" PRIu64,
934 static_cast<uint64_t>(mask & RBD_FEATURES_INTERNAL));
935 return -EINVAL;
936 }
937
938 // newer clients might attempt to mask off features we don't support
939 mask &= RBD_FEATURES_ALL;
940
941 uint64_t enabled_features = features & mask;
942 if ((enabled_features & RBD_FEATURES_MUTABLE) != enabled_features) {
943 CLS_ERR("Attempting to enable immutable feature: %" PRIu64,
944 static_cast<uint64_t>(enabled_features & ~RBD_FEATURES_MUTABLE));
945 return -EINVAL;
946 }
947
948 uint64_t disabled_features = ~features & mask;
949 uint64_t disable_mask = (RBD_FEATURES_MUTABLE | RBD_FEATURES_DISABLE_ONLY);
950 if ((disabled_features & disable_mask) != disabled_features) {
951 CLS_ERR("Attempting to disable immutable feature: %" PRIu64,
952 enabled_features & ~disable_mask);
953 return -EINVAL;
954 }
955
956 features = (orig_features & ~mask) | (features & mask);
957 CLS_LOG(10, "set_features features=%" PRIu64 " orig_features=%" PRIu64,
958 features, orig_features);
959
960 bufferlist bl;
961 encode(features, bl);
962 r = cls_cxx_map_set_val(hctx, "features", &bl);
963 if (r < 0) {
964 CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
965 return r;
966 }
967 return 0;
968 }
969
970 /**
971 * Input:
972 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
973 *
974 * Output:
975 * @param order bits to shift to get the size of data objects (uint8_t)
976 * @param size size of the image in bytes for the given snapshot (uint64_t)
977 * @returns 0 on success, negative error code on failure
978 */
979 int get_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
980 {
981 uint64_t snap_id, size;
982 uint8_t order;
983
984 auto iter = in->cbegin();
985 try {
986 decode(snap_id, iter);
987 } catch (const ceph::buffer::error &err) {
988 return -EINVAL;
989 }
990
991 CLS_LOG(20, "get_size snap_id=%llu", (unsigned long long)snap_id);
992
993 int r = read_key(hctx, "order", &order);
994 if (r < 0) {
995 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str());
996 return r;
997 }
998
999 if (snap_id == CEPH_NOSNAP) {
1000 r = read_key(hctx, "size", &size);
1001 if (r < 0) {
1002 CLS_ERR("failed to read the image's size off of disk: %s", cpp_strerror(r).c_str());
1003 return r;
1004 }
1005 } else {
1006 cls_rbd_snap snap;
1007 string snapshot_key;
1008 key_from_snap_id(snap_id, &snapshot_key);
1009 int r = read_key(hctx, snapshot_key, &snap);
1010 if (r < 0)
1011 return r;
1012
1013 size = snap.image_size;
1014 }
1015
1016 encode(order, *out);
1017 encode(size, *out);
1018
1019 return 0;
1020 }
1021
1022 /**
1023 * Input:
1024 * @param size new capacity of the image in bytes (uint64_t)
1025 *
1026 * Output:
1027 * @returns 0 on success, negative error code on failure
1028 */
1029 int set_size(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1030 {
1031 uint64_t size;
1032
1033 auto iter = in->cbegin();
1034 try {
1035 decode(size, iter);
1036 } catch (const ceph::buffer::error &err) {
1037 return -EINVAL;
1038 }
1039
1040 // check that size exists to make sure this is a header object
1041 // that was created correctly
1042 uint64_t orig_size;
1043 int r = read_key(hctx, "size", &orig_size);
1044 if (r < 0) {
1045 CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r).c_str());
1046 return r;
1047 }
1048
1049 CLS_LOG(20, "set_size size=%llu orig_size=%llu", (unsigned long long)size,
1050 (unsigned long long)orig_size);
1051
1052 bufferlist sizebl;
1053 encode(size, sizebl);
1054 r = cls_cxx_map_set_val(hctx, "size", &sizebl);
1055 if (r < 0) {
1056 CLS_ERR("error writing snapshot metadata: %s", cpp_strerror(r).c_str());
1057 return r;
1058 }
1059
1060 // if we are shrinking, and have a parent, shrink our overlap with
1061 // the parent, too.
1062 if (size < orig_size) {
1063 cls_rbd_parent parent;
1064 r = read_key(hctx, "parent", &parent);
1065 if (r == -ENOENT)
1066 r = 0;
1067 if (r < 0)
1068 return r;
1069 if (parent.exists() && parent.head_overlap.value_or(0ULL) > size) {
1070 parent.head_overlap = size;
1071 r = write_key(hctx, "parent", parent, get_encode_features(hctx));
1072 if (r < 0) {
1073 return r;
1074 }
1075 }
1076 }
1077
1078 return 0;
1079 }
1080
1081 /**
1082 * get the current protection status of the specified snapshot
1083 *
1084 * Input:
1085 * @param snap_id (uint64_t) which snapshot to get the status of
1086 *
1087 * Output:
1088 * @param status (uint8_t) one of:
1089 * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
1090 *
1091 * @returns 0 on success, negative error code on failure
1092 * @returns -EINVAL if snapid is CEPH_NOSNAP
1093 */
1094 int get_protection_status(cls_method_context_t hctx, bufferlist *in,
1095 bufferlist *out)
1096 {
1097 snapid_t snap_id;
1098
1099 auto iter = in->cbegin();
1100 try {
1101 decode(snap_id, iter);
1102 } catch (const ceph::buffer::error &err) {
1103 CLS_LOG(20, "get_protection_status: invalid decode");
1104 return -EINVAL;
1105 }
1106
1107 int r = check_exists(hctx);
1108 if (r < 0)
1109 return r;
1110
1111 CLS_LOG(20, "get_protection_status snap_id=%llu",
1112 (unsigned long long)snap_id.val);
1113
1114 if (snap_id == CEPH_NOSNAP)
1115 return -EINVAL;
1116
1117 cls_rbd_snap snap;
1118 string snapshot_key;
1119 key_from_snap_id(snap_id.val, &snapshot_key);
1120 r = read_key(hctx, snapshot_key, &snap);
1121 if (r < 0) {
1122 CLS_ERR("could not read key for snapshot id %" PRIu64, snap_id.val);
1123 return r;
1124 }
1125
1126 if (snap.protection_status >= RBD_PROTECTION_STATUS_LAST) {
1127 CLS_ERR("invalid protection status for snap id %llu: %u",
1128 (unsigned long long)snap_id.val, snap.protection_status);
1129 return -EIO;
1130 }
1131
1132 encode(snap.protection_status, *out);
1133 return 0;
1134 }
1135
1136 /**
1137 * set the proctection status of a snapshot
1138 *
1139 * Input:
1140 * @param snapid (uint64_t) which snapshot to set the status of
1141 * @param status (uint8_t) one of:
1142 * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
1143 *
1144 * @returns 0 on success, negative error code on failure
1145 * @returns -EINVAL if snapid is CEPH_NOSNAP
1146 */
1147 int set_protection_status(cls_method_context_t hctx, bufferlist *in,
1148 bufferlist *out)
1149 {
1150 snapid_t snap_id;
1151 uint8_t status;
1152
1153 auto iter = in->cbegin();
1154 try {
1155 decode(snap_id, iter);
1156 decode(status, iter);
1157 } catch (const ceph::buffer::error &err) {
1158 CLS_LOG(20, "set_protection_status: invalid decode");
1159 return -EINVAL;
1160 }
1161
1162 int r = check_exists(hctx);
1163 if (r < 0)
1164 return r;
1165
1166 r = image::require_feature(hctx, RBD_FEATURE_LAYERING);
1167 if (r < 0) {
1168 CLS_LOG(20, "image does not support layering");
1169 return r;
1170 }
1171
1172 CLS_LOG(20, "set_protection_status snapid=%llu status=%u",
1173 (unsigned long long)snap_id.val, status);
1174
1175 if (snap_id == CEPH_NOSNAP)
1176 return -EINVAL;
1177
1178 if (status >= RBD_PROTECTION_STATUS_LAST) {
1179 CLS_LOG(10, "invalid protection status for snap id %llu: %u",
1180 (unsigned long long)snap_id.val, status);
1181 return -EINVAL;
1182 }
1183
1184 cls_rbd_snap snap;
1185 string snapshot_key;
1186 key_from_snap_id(snap_id.val, &snapshot_key);
1187 r = read_key(hctx, snapshot_key, &snap);
1188 if (r < 0) {
1189 CLS_ERR("could not read key for snapshot id %" PRIu64, snap_id.val);
1190 return r;
1191 }
1192
1193 snap.protection_status = status;
1194 r = image::snapshot::write(hctx, snapshot_key, std::move(snap));
1195 if (r < 0) {
1196 return r;
1197 }
1198
1199 return 0;
1200 }
1201
1202 /**
1203 * get striping parameters
1204 *
1205 * Input:
1206 * none
1207 *
1208 * Output:
1209 * @param stripe unit (bytes)
1210 * @param stripe count (num objects)
1211 *
1212 * @returns 0 on success
1213 */
1214 int get_stripe_unit_count(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1215 {
1216 int r = check_exists(hctx);
1217 if (r < 0)
1218 return r;
1219
1220 CLS_LOG(20, "get_stripe_unit_count");
1221
1222 r = image::require_feature(hctx, RBD_FEATURE_STRIPINGV2);
1223 if (r < 0)
1224 return r;
1225
1226 uint64_t stripe_unit = 0, stripe_count = 0;
1227 r = read_key(hctx, "stripe_unit", &stripe_unit);
1228 if (r == -ENOENT) {
1229 // default to object size
1230 uint8_t order;
1231 r = read_key(hctx, "order", &order);
1232 if (r < 0) {
1233 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str());
1234 return -EIO;
1235 }
1236 stripe_unit = 1ull << order;
1237 }
1238 if (r < 0)
1239 return r;
1240 r = read_key(hctx, "stripe_count", &stripe_count);
1241 if (r == -ENOENT) {
1242 // default to 1
1243 stripe_count = 1;
1244 r = 0;
1245 }
1246 if (r < 0)
1247 return r;
1248
1249 encode(stripe_unit, *out);
1250 encode(stripe_count, *out);
1251 return 0;
1252 }
1253
1254 /**
1255 * set striping parameters
1256 *
1257 * Input:
1258 * @param stripe unit (bytes)
1259 * @param stripe count (num objects)
1260 *
1261 * @returns 0 on success
1262 */
1263 int set_stripe_unit_count(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1264 {
1265 uint64_t stripe_unit, stripe_count;
1266
1267 auto iter = in->cbegin();
1268 try {
1269 decode(stripe_unit, iter);
1270 decode(stripe_count, iter);
1271 } catch (const ceph::buffer::error &err) {
1272 CLS_LOG(20, "set_stripe_unit_count: invalid decode");
1273 return -EINVAL;
1274 }
1275
1276 if (!stripe_count || !stripe_unit)
1277 return -EINVAL;
1278
1279 int r = check_exists(hctx);
1280 if (r < 0)
1281 return r;
1282
1283 CLS_LOG(20, "set_stripe_unit_count");
1284
1285 r = image::require_feature(hctx, RBD_FEATURE_STRIPINGV2);
1286 if (r < 0)
1287 return r;
1288
1289 uint8_t order;
1290 r = read_key(hctx, "order", &order);
1291 if (r < 0) {
1292 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r).c_str());
1293 return r;
1294 }
1295 if ((1ull << order) % stripe_unit || stripe_unit > (1ull << order)) {
1296 CLS_ERR("stripe unit %llu is not a factor of the object size %llu",
1297 (unsigned long long)stripe_unit, 1ull << order);
1298 return -EINVAL;
1299 }
1300
1301 bufferlist bl, bl2;
1302 encode(stripe_unit, bl);
1303 r = cls_cxx_map_set_val(hctx, "stripe_unit", &bl);
1304 if (r < 0) {
1305 CLS_ERR("error writing stripe_unit metadata: %s", cpp_strerror(r).c_str());
1306 return r;
1307 }
1308
1309 encode(stripe_count, bl2);
1310 r = cls_cxx_map_set_val(hctx, "stripe_count", &bl2);
1311 if (r < 0) {
1312 CLS_ERR("error writing stripe_count metadata: %s", cpp_strerror(r).c_str());
1313 return r;
1314 }
1315
1316 return 0;
1317 }
1318
1319 int get_create_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1320 {
1321 CLS_LOG(20, "get_create_timestamp");
1322
1323 utime_t timestamp;
1324 bufferlist bl;
1325 int r = cls_cxx_map_get_val(hctx, "create_timestamp", &bl);
1326 if (r < 0) {
1327 if (r != -ENOENT) {
1328 CLS_ERR("error reading create_timestamp: %s", cpp_strerror(r).c_str());
1329 return r;
1330 }
1331 } else {
1332 try {
1333 auto it = bl.cbegin();
1334 decode(timestamp, it);
1335 } catch (const ceph::buffer::error &err) {
1336 CLS_ERR("could not decode create_timestamp");
1337 return -EIO;
1338 }
1339 }
1340
1341 encode(timestamp, *out);
1342 return 0;
1343 }
1344
1345 /**
1346 * get the image access timestamp
1347 *
1348 * Input:
1349 * @param none
1350 *
1351 * Output:
1352 * @param timestamp the image access timestamp
1353 *
1354 * @returns 0 on success, negative error code upon failure
1355 */
1356 int get_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1357 {
1358 CLS_LOG(20, "get_access_timestamp");
1359
1360 utime_t timestamp;
1361 bufferlist bl;
1362 int r = cls_cxx_map_get_val(hctx, "access_timestamp", &bl);
1363 if (r < 0) {
1364 if (r != -ENOENT) {
1365 CLS_ERR("error reading access_timestamp: %s", cpp_strerror(r).c_str());
1366 return r;
1367 }
1368 } else {
1369 try {
1370 auto it = bl.cbegin();
1371 decode(timestamp, it);
1372 } catch (const ceph::buffer::error &err) {
1373 CLS_ERR("could not decode access_timestamp");
1374 return -EIO;
1375 }
1376 }
1377
1378 encode(timestamp, *out);
1379 return 0;
1380 }
1381
1382 /**
1383 * get the image modify timestamp
1384 *
1385 * Input:
1386 * @param none
1387 *
1388 * Output:
1389 * @param timestamp the image modify timestamp
1390 *
1391 * @returns 0 on success, negative error code upon failure
1392 */
1393 int get_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1394 {
1395 CLS_LOG(20, "get_modify_timestamp");
1396
1397 utime_t timestamp;
1398 bufferlist bl;
1399 int r = cls_cxx_map_get_val(hctx, "modify_timestamp", &bl);
1400 if (r < 0) {
1401 if (r != -ENOENT) {
1402 CLS_ERR("error reading modify_timestamp: %s", cpp_strerror(r).c_str());
1403 return r;
1404 }
1405 } else {
1406 try {
1407 auto it = bl.cbegin();
1408 decode(timestamp, it);
1409 } catch (const ceph::buffer::error &err) {
1410 CLS_ERR("could not decode modify_timestamp");
1411 return -EIO;
1412 }
1413 }
1414
1415 encode(timestamp, *out);
1416 return 0;
1417 }
1418
1419
1420 /**
1421 * get the image flags
1422 *
1423 * Input:
1424 * @param snap_id which snapshot to query, to CEPH_NOSNAP (uint64_t)
1425 *
1426 * Output:
1427 * @param flags image flags
1428 *
1429 * @returns 0 on success, negative error code upon failure
1430 */
1431 int get_flags(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1432 {
1433 uint64_t snap_id;
1434 auto iter = in->cbegin();
1435 try {
1436 decode(snap_id, iter);
1437 } catch (const ceph::buffer::error &err) {
1438 return -EINVAL;
1439 }
1440
1441 CLS_LOG(20, "get_flags snap_id=%llu", (unsigned long long)snap_id);
1442
1443 uint64_t flags = 0;
1444 if (snap_id == CEPH_NOSNAP) {
1445 int r = read_key(hctx, "flags", &flags);
1446 if (r < 0 && r != -ENOENT) {
1447 CLS_ERR("failed to read flags off disk: %s", cpp_strerror(r).c_str());
1448 return r;
1449 }
1450 } else {
1451 cls_rbd_snap snap;
1452 string snapshot_key;
1453 key_from_snap_id(snap_id, &snapshot_key);
1454 int r = read_key(hctx, snapshot_key, &snap);
1455 if (r < 0) {
1456 return r;
1457 }
1458 flags = snap.flags;
1459 }
1460
1461 encode(flags, *out);
1462 return 0;
1463 }
1464
1465 /**
1466 * set the image flags
1467 *
1468 * Input:
1469 * @param flags image flags
1470 * @param mask image flag mask
1471 * @param snap_id which snapshot to update, or CEPH_NOSNAP (uint64_t)
1472 *
1473 * Output:
1474 * none
1475 *
1476 * @returns 0 on success, negative error code upon failure
1477 */
1478 int set_flags(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1479 {
1480 uint64_t flags;
1481 uint64_t mask;
1482 uint64_t snap_id = CEPH_NOSNAP;
1483 auto iter = in->cbegin();
1484 try {
1485 decode(flags, iter);
1486 decode(mask, iter);
1487 if (!iter.end()) {
1488 decode(snap_id, iter);
1489 }
1490 } catch (const ceph::buffer::error &err) {
1491 return -EINVAL;
1492 }
1493
1494 // check that size exists to make sure this is a header object
1495 // that was created correctly
1496 int r;
1497 uint64_t orig_flags = 0;
1498 cls_rbd_snap snap_meta;
1499 string snap_meta_key;
1500 if (snap_id == CEPH_NOSNAP) {
1501 r = read_key(hctx, "flags", &orig_flags);
1502 if (r < 0 && r != -ENOENT) {
1503 CLS_ERR("Could not read image's flags off disk: %s",
1504 cpp_strerror(r).c_str());
1505 return r;
1506 }
1507 } else {
1508 key_from_snap_id(snap_id, &snap_meta_key);
1509 r = read_key(hctx, snap_meta_key, &snap_meta);
1510 if (r < 0) {
1511 CLS_ERR("Could not read snapshot: snap_id=%" PRIu64 ": %s",
1512 snap_id, cpp_strerror(r).c_str());
1513 return r;
1514 }
1515 orig_flags = snap_meta.flags;
1516 }
1517
1518 flags = (orig_flags & ~mask) | (flags & mask);
1519 CLS_LOG(20, "set_flags snap_id=%" PRIu64 ", orig_flags=%" PRIu64 ", "
1520 "new_flags=%" PRIu64 ", mask=%" PRIu64, snap_id, orig_flags,
1521 flags, mask);
1522
1523 if (snap_id == CEPH_NOSNAP) {
1524 r = write_key(hctx, "flags", flags);
1525 } else {
1526 snap_meta.flags = flags;
1527 r = image::snapshot::write(hctx, snap_meta_key, std::move(snap_meta));
1528 }
1529
1530 if (r < 0) {
1531 return r;
1532 }
1533 return 0;
1534 }
1535
1536 /**
1537 * Get the operation-based image features
1538 *
1539 * Input:
1540 *
1541 * Output:
1542 * @param bitmask of enabled op features (uint64_t)
1543 * @returns 0 on success, negative error code on failure
1544 */
1545 int op_features_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1546 {
1547 CLS_LOG(20, "op_features_get");
1548
1549 uint64_t op_features = 0;
1550 int r = read_key(hctx, "op_features", &op_features);
1551 if (r < 0 && r != -ENOENT) {
1552 CLS_ERR("failed to read op features off disk: %s", cpp_strerror(r).c_str());
1553 return r;
1554 }
1555
1556 encode(op_features, *out);
1557 return 0;
1558 }
1559
1560 /**
1561 * Set the operation-based image features
1562 *
1563 * Input:
1564 * @param op_features image op features
1565 * @param mask image op feature mask
1566 *
1567 * Output:
1568 * none
1569 *
1570 * @returns 0 on success, negative error code upon failure
1571 */
1572 int op_features_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1573 {
1574 uint64_t op_features;
1575 uint64_t mask;
1576 auto iter = in->cbegin();
1577 try {
1578 decode(op_features, iter);
1579 decode(mask, iter);
1580 } catch (const ceph::buffer::error &err) {
1581 return -EINVAL;
1582 }
1583
1584 uint64_t unsupported_op_features = (mask & ~RBD_OPERATION_FEATURES_ALL);
1585 if (unsupported_op_features != 0ULL) {
1586 CLS_ERR("unsupported op features: %" PRIu64, unsupported_op_features);
1587 return -EINVAL;
1588 }
1589
1590 return image::set_op_features(hctx, op_features, mask);
1591 }
1592
1593 /**
1594 * get the current parent, if any
1595 *
1596 * Input:
1597 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
1598 *
1599 * Output:
1600 * @param pool parent pool id (-1 if parent does not exist)
1601 * @param image parent image id
1602 * @param snapid parent snapid
1603 * @param size portion of parent mapped under the child
1604 *
1605 * @returns 0 on success or parent does not exist, negative error code on failure
1606 */
1607 int get_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1608 {
1609 uint64_t snap_id;
1610
1611 auto iter = in->cbegin();
1612 try {
1613 decode(snap_id, iter);
1614 } catch (const ceph::buffer::error &err) {
1615 return -EINVAL;
1616 }
1617
1618 int r = check_exists(hctx);
1619 if (r < 0) {
1620 return r;
1621 }
1622
1623 CLS_LOG(20, "get_parent snap_id=%" PRIu64, snap_id);
1624
1625 cls_rbd_parent parent;
1626 r = image::require_feature(hctx, RBD_FEATURE_LAYERING);
1627 if (r == 0) {
1628 r = read_key(hctx, "parent", &parent);
1629 if (r < 0 && r != -ENOENT) {
1630 return r;
1631 } else if (!parent.pool_namespace.empty()) {
1632 return -EXDEV;
1633 }
1634
1635 if (snap_id != CEPH_NOSNAP) {
1636 cls_rbd_snap snap;
1637 std::string snapshot_key;
1638 key_from_snap_id(snap_id, &snapshot_key);
1639 r = read_key(hctx, snapshot_key, &snap);
1640 if (r < 0 && r != -ENOENT) {
1641 return r;
1642 }
1643
1644 if (snap.parent.exists()) {
1645 // legacy format where full parent spec is written within
1646 // each snapshot record
1647 parent = snap.parent;
1648 } else if (snap.parent_overlap) {
1649 // normalized parent reference
1650 if (!parent.exists()) {
1651 CLS_ERR("get_parent: snap_id=%" PRIu64 ": invalid parent spec",
1652 snap_id);
1653 return -EINVAL;
1654 }
1655 parent.head_overlap = *snap.parent_overlap;
1656 } else {
1657 // snapshot doesn't have associated parent
1658 parent = {};
1659 }
1660 }
1661 }
1662
1663 encode(parent.pool_id, *out);
1664 encode(parent.image_id, *out);
1665 encode(parent.snap_id, *out);
1666 encode(parent.head_overlap.value_or(0ULL), *out);
1667 return 0;
1668 }
1669
1670 /**
1671 * set the image parent
1672 *
1673 * Input:
1674 * @param pool parent pool
1675 * @param id parent image id
1676 * @param snapid parent snapid
1677 * @param size parent size
1678 *
1679 * @returns 0 on success, or negative error code
1680 */
1681 int set_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1682 {
1683 cls_rbd_parent parent;
1684 auto iter = in->cbegin();
1685 try {
1686 decode(parent.pool_id, iter);
1687 decode(parent.image_id, iter);
1688 decode(parent.snap_id, iter);
1689
1690 uint64_t overlap;
1691 decode(overlap, iter);
1692 parent.head_overlap = overlap;
1693 } catch (const ceph::buffer::error &err) {
1694 CLS_LOG(20, "cls_rbd::set_parent: invalid decode");
1695 return -EINVAL;
1696 }
1697
1698 int r = image::parent::attach(hctx, parent, false);
1699 if (r < 0) {
1700 return r;
1701 }
1702
1703 return 0;
1704 }
1705
1706
1707 /**
1708 * remove the parent pointer
1709 *
1710 * This can only happen on the head, not on a snapshot. No arguments.
1711 *
1712 * @returns 0 on success, negative error code on failure.
1713 */
1714 int remove_parent(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1715 {
1716 int r = image::parent::detach(hctx, true);
1717 if (r < 0) {
1718 return r;
1719 }
1720
1721 return 0;
1722 }
1723
1724 /**
1725 * Input:
1726 * none
1727 *
1728 * Output:
1729 * @param parent spec (cls::rbd::ParentImageSpec)
1730 * @returns 0 on success, negative error code on failure
1731 */
1732 int parent_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
1733 int r = check_exists(hctx);
1734 if (r < 0) {
1735 return r;
1736 }
1737
1738 CLS_LOG(20, "parent_get");
1739
1740 cls_rbd_parent parent;
1741 r = image::require_feature(hctx, RBD_FEATURE_LAYERING);
1742 if (r == 0) {
1743 r = read_key(hctx, "parent", &parent);
1744 if (r < 0 && r != -ENOENT) {
1745 return r;
1746 } else if (r == -ENOENT) {
1747 // examine oldest snapshot to see if it has a denormalized parent
1748 auto parent_lambda = [&parent](const cls_rbd_snap& snap_meta) {
1749 if (snap_meta.parent.exists()) {
1750 parent = snap_meta.parent;
1751 }
1752 return 0;
1753 };
1754
1755 r = image::snapshot::iterate(hctx, parent_lambda);
1756 if (r < 0) {
1757 return r;
1758 }
1759 }
1760 }
1761
1762 cls::rbd::ParentImageSpec parent_image_spec{
1763 parent.pool_id, parent.pool_namespace, parent.image_id,
1764 parent.snap_id};
1765 encode(parent_image_spec, *out);
1766 return 0;
1767 }
1768
1769 /**
1770 * Input:
1771 * @param snap id (uint64_t) parent snapshot id
1772 *
1773 * Output:
1774 * @param byte overlap of parent image (std::optional<uint64_t>)
1775 * @returns 0 on success, negative error code on failure
1776 */
1777 int parent_overlap_get(cls_method_context_t hctx, bufferlist *in,
1778 bufferlist *out) {
1779 uint64_t snap_id;
1780 auto iter = in->cbegin();
1781 try {
1782 decode(snap_id, iter);
1783 } catch (const ceph::buffer::error &err) {
1784 return -EINVAL;
1785 }
1786
1787 int r = check_exists(hctx);
1788 CLS_LOG(20, "parent_overlap_get");
1789
1790 std::optional<uint64_t> parent_overlap = std::nullopt;
1791 r = image::require_feature(hctx, RBD_FEATURE_LAYERING);
1792 if (r == 0) {
1793 if (snap_id == CEPH_NOSNAP) {
1794 cls_rbd_parent parent;
1795 r = read_key(hctx, "parent", &parent);
1796 if (r < 0 && r != -ENOENT) {
1797 return r;
1798 } else if (r == 0) {
1799 parent_overlap = parent.head_overlap;
1800 }
1801 } else {
1802 cls_rbd_snap snap;
1803 std::string snapshot_key;
1804 key_from_snap_id(snap_id, &snapshot_key);
1805 r = read_key(hctx, snapshot_key, &snap);
1806 if (r < 0) {
1807 return r;
1808 }
1809
1810 if (snap.parent_overlap) {
1811 parent_overlap = snap.parent_overlap;
1812 } else if (snap.parent.exists()) {
1813 // legacy format where full parent spec is written within
1814 // each snapshot record
1815 parent_overlap = snap.parent.head_overlap;
1816 }
1817 }
1818 };
1819
1820 encode(parent_overlap, *out);
1821 return 0;
1822 }
1823
1824 /**
1825 * Input:
1826 * @param parent spec (cls::rbd::ParentImageSpec)
1827 * @param size parent size (uint64_t)
1828 *
1829 * Output:
1830 * @returns 0 on success, negative error code on failure
1831 */
1832 int parent_attach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
1833 cls::rbd::ParentImageSpec parent_image_spec;
1834 uint64_t parent_overlap;
1835 bool reattach = false;
1836
1837 auto iter = in->cbegin();
1838 try {
1839 decode(parent_image_spec, iter);
1840 decode(parent_overlap, iter);
1841 if (!iter.end()) {
1842 decode(reattach, iter);
1843 }
1844 } catch (const ceph::buffer::error &err) {
1845 CLS_LOG(20, "cls_rbd::parent_attach: invalid decode");
1846 return -EINVAL;
1847 }
1848
1849 int r = image::parent::attach(hctx, {parent_image_spec, parent_overlap},
1850 reattach);
1851 if (r < 0) {
1852 return r;
1853 }
1854
1855 return 0;
1856 }
1857
1858 /**
1859 * Input:
1860 * none
1861 *
1862 * Output:
1863 * @returns 0 on success, negative error code on failure
1864 */
1865 int parent_detach(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
1866 int r = image::parent::detach(hctx, false);
1867 if (r < 0) {
1868 return r;
1869 }
1870
1871 return 0;
1872 }
1873
1874
1875 /**
1876 * methods for dealing with rbd_children object
1877 */
1878
1879 static int decode_parent_common(bufferlist::const_iterator& it, uint64_t *pool_id,
1880 string *image_id, snapid_t *snap_id)
1881 {
1882 try {
1883 decode(*pool_id, it);
1884 decode(*image_id, it);
1885 decode(*snap_id, it);
1886 } catch (const ceph::buffer::error &err) {
1887 CLS_ERR("error decoding parent spec");
1888 return -EINVAL;
1889 }
1890 return 0;
1891 }
1892
1893 static int decode_parent(bufferlist *in, uint64_t *pool_id,
1894 string *image_id, snapid_t *snap_id)
1895 {
1896 auto it = in->cbegin();
1897 return decode_parent_common(it, pool_id, image_id, snap_id);
1898 }
1899
1900 static int decode_parent_and_child(bufferlist *in, uint64_t *pool_id,
1901 string *image_id, snapid_t *snap_id,
1902 string *c_image_id)
1903 {
1904 auto it = in->cbegin();
1905 int r = decode_parent_common(it, pool_id, image_id, snap_id);
1906 if (r < 0)
1907 return r;
1908 try {
1909 decode(*c_image_id, it);
1910 } catch (const ceph::buffer::error &err) {
1911 CLS_ERR("error decoding child image id");
1912 return -EINVAL;
1913 }
1914 return 0;
1915 }
1916
1917 static string parent_key(uint64_t pool_id, string image_id, snapid_t snap_id)
1918 {
1919 bufferlist key_bl;
1920 encode(pool_id, key_bl);
1921 encode(image_id, key_bl);
1922 encode(snap_id, key_bl);
1923 return string(key_bl.c_str(), key_bl.length());
1924 }
1925
1926 /**
1927 * add child to rbd_children directory object
1928 *
1929 * rbd_children is a map of (p_pool_id, p_image_id, p_snap_id) to
1930 * [c_image_id, [c_image_id ... ]]
1931 *
1932 * Input:
1933 * @param p_pool_id parent pool id
1934 * @param p_image_id parent image oid
1935 * @param p_snap_id parent snapshot id
1936 * @param c_image_id new child image oid to add
1937 *
1938 * @returns 0 on success, negative error on failure
1939 */
1940
1941 int add_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1942 {
1943 int r;
1944
1945 uint64_t p_pool_id;
1946 snapid_t p_snap_id;
1947 string p_image_id, c_image_id;
1948 // Use set for ease of erase() for remove_child()
1949 std::set<string> children;
1950
1951 r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id,
1952 &c_image_id);
1953 if (r < 0)
1954 return r;
1955
1956 CLS_LOG(20, "add_child %s to (%" PRIu64 ", %s, %" PRIu64 ")", c_image_id.c_str(),
1957 p_pool_id, p_image_id.c_str(), p_snap_id.val);
1958
1959 string key = parent_key(p_pool_id, p_image_id, p_snap_id);
1960
1961 // get current child list for parent, if any
1962 r = read_key(hctx, key, &children);
1963 if ((r < 0) && (r != -ENOENT)) {
1964 CLS_LOG(20, "add_child: omap read failed: %s", cpp_strerror(r).c_str());
1965 return r;
1966 }
1967
1968 if (children.find(c_image_id) != children.end()) {
1969 CLS_LOG(20, "add_child: child already exists: %s", c_image_id.c_str());
1970 return -EEXIST;
1971 }
1972 // add new child
1973 children.insert(c_image_id);
1974
1975 // write back
1976 bufferlist childbl;
1977 encode(children, childbl);
1978 r = cls_cxx_map_set_val(hctx, key, &childbl);
1979 if (r < 0)
1980 CLS_LOG(20, "add_child: omap write failed: %s", cpp_strerror(r).c_str());
1981 return r;
1982 }
1983
1984 /**
1985 * remove child from rbd_children directory object
1986 *
1987 * Input:
1988 * @param p_pool_id parent pool id
1989 * @param p_image_id parent image oid
1990 * @param p_snap_id parent snapshot id
1991 * @param c_image_id new child image oid to add
1992 *
1993 * @returns 0 on success, negative error on failure
1994 */
1995
1996 int remove_child(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
1997 {
1998 int r;
1999
2000 uint64_t p_pool_id;
2001 snapid_t p_snap_id;
2002 string p_image_id, c_image_id;
2003 std::set<string> children;
2004
2005 r = decode_parent_and_child(in, &p_pool_id, &p_image_id, &p_snap_id,
2006 &c_image_id);
2007 if (r < 0)
2008 return r;
2009
2010 CLS_LOG(20, "remove_child %s from (%" PRIu64 ", %s, %" PRIu64 ")",
2011 c_image_id.c_str(), p_pool_id, p_image_id.c_str(),
2012 p_snap_id.val);
2013
2014 string key = parent_key(p_pool_id, p_image_id, p_snap_id);
2015
2016 // get current child list for parent. Unlike add_child(), an empty list
2017 // is an error (how can we remove something that doesn't exist?)
2018 r = read_key(hctx, key, &children);
2019 if (r < 0) {
2020 CLS_LOG(20, "remove_child: read omap failed: %s", cpp_strerror(r).c_str());
2021 return r;
2022 }
2023
2024 if (children.find(c_image_id) == children.end()) {
2025 CLS_LOG(20, "remove_child: child not found: %s", c_image_id.c_str());
2026 return -ENOENT;
2027 }
2028 // find and remove child
2029 children.erase(c_image_id);
2030
2031 // now empty? remove key altogether
2032 if (children.empty()) {
2033 r = cls_cxx_map_remove_key(hctx, key);
2034 if (r < 0)
2035 CLS_LOG(20, "remove_child: remove key failed: %s", cpp_strerror(r).c_str());
2036 } else {
2037 // write back shortened children list
2038 bufferlist childbl;
2039 encode(children, childbl);
2040 r = cls_cxx_map_set_val(hctx, key, &childbl);
2041 if (r < 0)
2042 CLS_LOG(20, "remove_child: write omap failed: %s", cpp_strerror(r).c_str());
2043 }
2044 return r;
2045 }
2046
2047 /**
2048 * Input:
2049 * @param p_pool_id parent pool id
2050 * @param p_image_id parent image oid
2051 * @param p_snap_id parent snapshot id
2052 * @param c_image_id new child image oid to add
2053 *
2054 * Output:
2055 * @param children set<string> of children
2056 *
2057 * @returns 0 on success, negative error on failure
2058 */
2059 int get_children(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2060 {
2061 int r;
2062 uint64_t p_pool_id;
2063 snapid_t p_snap_id;
2064 string p_image_id;
2065 std::set<string> children;
2066
2067 r = decode_parent(in, &p_pool_id, &p_image_id, &p_snap_id);
2068 if (r < 0)
2069 return r;
2070
2071 CLS_LOG(20, "get_children of (%" PRIu64 ", %s, %" PRIu64 ")",
2072 p_pool_id, p_image_id.c_str(), p_snap_id.val);
2073
2074 string key = parent_key(p_pool_id, p_image_id, p_snap_id);
2075
2076 r = read_key(hctx, key, &children);
2077 if (r < 0) {
2078 if (r != -ENOENT)
2079 CLS_LOG(20, "get_children: read omap failed: %s", cpp_strerror(r).c_str());
2080 return r;
2081 }
2082 encode(children, *out);
2083 return 0;
2084 }
2085
2086
2087 /**
2088 * Get the information needed to create a rados snap context for doing
2089 * I/O to the data objects. This must include all snapshots.
2090 *
2091 * Output:
2092 * @param snap_seq the highest snapshot id ever associated with the image (uint64_t)
2093 * @param snap_ids existing snapshot ids in descending order (vector<uint64_t>)
2094 * @returns 0 on success, negative error code on failure
2095 */
2096 int get_snapcontext(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2097 {
2098 CLS_LOG(20, "get_snapcontext");
2099
2100 int r;
2101 int max_read = RBD_MAX_KEYS_READ;
2102 vector<snapid_t> snap_ids;
2103 string last_read = RBD_SNAP_KEY_PREFIX;
2104 bool more;
2105
2106 do {
2107 set<string> keys;
2108 r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more);
2109 if (r < 0)
2110 return r;
2111
2112 for (auto it = keys.begin(); it != keys.end(); ++it) {
2113 if ((*it).find(RBD_SNAP_KEY_PREFIX) != 0)
2114 break;
2115 snapid_t snap_id = snap_id_from_key(*it);
2116 snap_ids.push_back(snap_id);
2117 }
2118 if (!keys.empty())
2119 last_read = *(keys.rbegin());
2120 } while (more);
2121
2122 uint64_t snap_seq;
2123 r = read_key(hctx, "snap_seq", &snap_seq);
2124 if (r < 0) {
2125 CLS_ERR("could not read the image's snap_seq off disk: %s", cpp_strerror(r).c_str());
2126 return r;
2127 }
2128
2129 // snap_ids must be descending in a snap context
2130 std::reverse(snap_ids.begin(), snap_ids.end());
2131
2132 encode(snap_seq, *out);
2133 encode(snap_ids, *out);
2134
2135 return 0;
2136 }
2137
2138 /**
2139 * Output:
2140 * @param object_prefix prefix for data object names (string)
2141 * @returns 0 on success, negative error code on failure
2142 */
2143 int get_object_prefix(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2144 {
2145 CLS_LOG(20, "get_object_prefix");
2146
2147 string object_prefix;
2148 int r = read_key(hctx, "object_prefix", &object_prefix);
2149 if (r < 0) {
2150 CLS_ERR("failed to read the image's object prefix off of disk: %s",
2151 cpp_strerror(r).c_str());
2152 return r;
2153 }
2154
2155 encode(object_prefix, *out);
2156
2157 return 0;
2158 }
2159
2160 /**
2161 * Input:
2162 * none
2163 *
2164 * Output:
2165 * @param pool_id (int64_t) of data pool or -1 if none
2166 * @returns 0 on success, negative error code on failure
2167 */
2168 int get_data_pool(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2169 {
2170 CLS_LOG(20, "get_data_pool");
2171
2172 int64_t data_pool_id = -1;
2173 int r = read_key(hctx, "data_pool_id", &data_pool_id);
2174 if (r == -ENOENT) {
2175 data_pool_id = -1;
2176 } else if (r < 0) {
2177 CLS_ERR("error reading image data pool id: %s", cpp_strerror(r).c_str());
2178 return r;
2179 }
2180
2181 encode(data_pool_id, *out);
2182 return 0;
2183 }
2184
2185 /**
2186 * Input:
2187 * @param snap_id which snapshot to query
2188 *
2189 * Output:
2190 * @param name (string) of the snapshot
2191 * @returns 0 on success, negative error code on failure
2192 */
2193 int get_snapshot_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2194 {
2195 uint64_t snap_id;
2196
2197 auto iter = in->cbegin();
2198 try {
2199 decode(snap_id, iter);
2200 } catch (const ceph::buffer::error &err) {
2201 return -EINVAL;
2202 }
2203
2204 CLS_LOG(20, "get_snapshot_name snap_id=%llu", (unsigned long long)snap_id);
2205
2206 if (snap_id == CEPH_NOSNAP)
2207 return -EINVAL;
2208
2209 cls_rbd_snap snap;
2210 string snapshot_key;
2211 key_from_snap_id(snap_id, &snapshot_key);
2212 int r = read_key(hctx, snapshot_key, &snap);
2213 if (r < 0)
2214 return r;
2215
2216 encode(snap.name, *out);
2217
2218 return 0;
2219 }
2220
2221 /**
2222 * Input:
2223 * @param snap_id which snapshot to query
2224 *
2225 * Output:
2226 * @param timestamp (utime_t) of the snapshot
2227 * @returns 0 on success, negative error code on failure
2228 *
2229 * NOTE: deprecated - remove this method after Luminous is unsupported
2230 */
2231 int get_snapshot_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2232 {
2233 uint64_t snap_id;
2234
2235 auto iter = in->cbegin();
2236 try {
2237 decode(snap_id, iter);
2238 } catch (const ceph::buffer::error &err) {
2239 return -EINVAL;
2240 }
2241
2242 CLS_LOG(20, "get_snapshot_timestamp snap_id=%llu", (unsigned long long)snap_id);
2243
2244 if (snap_id == CEPH_NOSNAP) {
2245 return -EINVAL;
2246 }
2247
2248 cls_rbd_snap snap;
2249 string snapshot_key;
2250 key_from_snap_id(snap_id, &snapshot_key);
2251 int r = read_key(hctx, snapshot_key, &snap);
2252 if (r < 0) {
2253 return r;
2254 }
2255
2256 encode(snap.timestamp, *out);
2257 return 0;
2258 }
2259
2260 /**
2261 * Input:
2262 * @param snap_id which snapshot to query
2263 *
2264 * Output:
2265 * @param snapshot (cls::rbd::SnapshotInfo)
2266 * @returns 0 on success, negative error code on failure
2267 */
2268 int snapshot_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2269 {
2270 uint64_t snap_id;
2271
2272 auto iter = in->cbegin();
2273 try {
2274 decode(snap_id, iter);
2275 } catch (const ceph::buffer::error &err) {
2276 return -EINVAL;
2277 }
2278
2279 CLS_LOG(20, "snapshot_get snap_id=%llu", (unsigned long long)snap_id);
2280 if (snap_id == CEPH_NOSNAP) {
2281 return -EINVAL;
2282 }
2283
2284 cls_rbd_snap snap;
2285 string snapshot_key;
2286 key_from_snap_id(snap_id, &snapshot_key);
2287 int r = read_key(hctx, snapshot_key, &snap);
2288 if (r < 0) {
2289 return r;
2290 }
2291
2292 cls::rbd::SnapshotInfo snapshot_info{snap.id, snap.snapshot_namespace,
2293 snap.name, snap.image_size,
2294 snap.timestamp, snap.child_count};
2295 encode(snapshot_info, *out);
2296 return 0;
2297 }
2298
2299 /**
2300 * Adds a snapshot to an rbd header. Ensures the id and name are unique.
2301 *
2302 * Input:
2303 * @param snap_name name of the snapshot (string)
2304 * @param snap_id id of the snapshot (uint64_t)
2305 * @param snap_namespace namespace of the snapshot (cls::rbd::SnapshotNamespace)
2306 *
2307 * Output:
2308 * @returns 0 on success, negative error code on failure.
2309 * @returns -ESTALE if the input snap_id is less than the image's snap_seq
2310 * @returns -EEXIST if the id or name are already used by another snapshot
2311 */
2312 int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2313 {
2314 bufferlist snap_namebl, snap_idbl;
2315 cls_rbd_snap snap_meta;
2316 uint64_t snap_limit;
2317
2318 try {
2319 auto iter = in->cbegin();
2320 decode(snap_meta.name, iter);
2321 decode(snap_meta.id, iter);
2322 if (!iter.end()) {
2323 decode(snap_meta.snapshot_namespace, iter);
2324 }
2325 } catch (const ceph::buffer::error &err) {
2326 return -EINVAL;
2327 }
2328
2329 if (boost::get<cls::rbd::UnknownSnapshotNamespace>(
2330 &snap_meta.snapshot_namespace) != nullptr) {
2331 CLS_ERR("Unknown snapshot namespace provided");
2332 return -EINVAL;
2333 }
2334
2335 CLS_LOG(20, "snapshot_add name=%s id=%llu", snap_meta.name.c_str(),
2336 (unsigned long long)snap_meta.id.val);
2337
2338 if (snap_meta.id > CEPH_MAXSNAP)
2339 return -EINVAL;
2340
2341 uint64_t cur_snap_seq;
2342 int r = read_key(hctx, "snap_seq", &cur_snap_seq);
2343 if (r < 0) {
2344 CLS_ERR("Could not read image's snap_seq off disk: %s", cpp_strerror(r).c_str());
2345 return r;
2346 }
2347
2348 // client lost a race with another snapshot creation.
2349 // snap_seq must be monotonically increasing.
2350 if (snap_meta.id < cur_snap_seq)
2351 return -ESTALE;
2352
2353 r = read_key(hctx, "size", &snap_meta.image_size);
2354 if (r < 0) {
2355 CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r).c_str());
2356 return r;
2357 }
2358 r = read_key(hctx, "flags", &snap_meta.flags);
2359 if (r < 0 && r != -ENOENT) {
2360 CLS_ERR("Could not read image's flags off disk: %s", cpp_strerror(r).c_str());
2361 return r;
2362 }
2363
2364 r = read_key(hctx, "snap_limit", &snap_limit);
2365 if (r == -ENOENT) {
2366 snap_limit = UINT64_MAX;
2367 } else if (r < 0) {
2368 CLS_ERR("Could not read snapshot limit off disk: %s", cpp_strerror(r).c_str());
2369 return r;
2370 }
2371
2372 snap_meta.timestamp = ceph_clock_now();
2373
2374 uint64_t total_read = 0;
2375 auto pre_check_lambda =
2376 [&snap_meta, &total_read, snap_limit](const cls_rbd_snap& old_meta) {
2377 ++total_read;
2378 if (total_read >= snap_limit) {
2379 CLS_ERR("Attempt to create snapshot over limit of %" PRIu64,
2380 snap_limit);
2381 return -EDQUOT;
2382 }
2383
2384 if ((snap_meta.name == old_meta.name &&
2385 snap_meta.snapshot_namespace == old_meta.snapshot_namespace) ||
2386 snap_meta.id == old_meta.id) {
2387 CLS_LOG(20, "snap_name %s or snap_id %" PRIu64 " matches existing snap "
2388 "%s %" PRIu64, snap_meta.name.c_str(), snap_meta.id.val,
2389 old_meta.name.c_str(), old_meta.id.val);
2390 return -EEXIST;
2391 }
2392 return 0;
2393 };
2394
2395 r = image::snapshot::iterate(hctx, pre_check_lambda);
2396 if (r < 0) {
2397 return r;
2398 }
2399
2400 // snapshot inherits parent, if any
2401 cls_rbd_parent parent;
2402 r = read_key(hctx, "parent", &parent);
2403 if (r < 0 && r != -ENOENT) {
2404 return r;
2405 }
2406 if (r == 0) {
2407 // write helper method will convert to normalized format if required
2408 snap_meta.parent = parent;
2409 }
2410
2411 if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) ==
2412 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
2413 // add snap_trash feature bit if not already enabled
2414 r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_SNAP_TRASH,
2415 RBD_OPERATION_FEATURE_SNAP_TRASH);
2416 if (r < 0) {
2417 return r;
2418 }
2419 }
2420
2421 r = write_key(hctx, "snap_seq", snap_meta.id);
2422 if (r < 0) {
2423 return r;
2424 }
2425
2426 std::string snapshot_key;
2427 key_from_snap_id(snap_meta.id, &snapshot_key);
2428 r = image::snapshot::write(hctx, snapshot_key, std::move(snap_meta));
2429 if (r < 0) {
2430 return r;
2431 }
2432
2433 return 0;
2434 }
2435
2436 /**
2437 * rename snapshot .
2438 *
2439 * Input:
2440 * @param src_snap_id old snap id of the snapshot (snapid_t)
2441 * @param dst_snap_name new name of the snapshot (string)
2442 *
2443 * Output:
2444 * @returns 0 on success, negative error code on failure.
2445 */
2446 int snapshot_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2447 {
2448 bufferlist snap_namebl, snap_idbl;
2449 snapid_t src_snap_id;
2450 string dst_snap_name;
2451 cls_rbd_snap snap_meta;
2452 int r;
2453
2454 try {
2455 auto iter = in->cbegin();
2456 decode(src_snap_id, iter);
2457 decode(dst_snap_name, iter);
2458 } catch (const ceph::buffer::error &err) {
2459 return -EINVAL;
2460 }
2461
2462 CLS_LOG(20, "snapshot_rename id=%" PRIu64 ", dst_name=%s",
2463 src_snap_id.val, dst_snap_name.c_str());
2464
2465 auto duplicate_name_lambda = [&dst_snap_name](const cls_rbd_snap& snap_meta) {
2466 if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) ==
2467 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER &&
2468 snap_meta.name == dst_snap_name) {
2469 CLS_LOG(20, "snap_name %s matches existing snap with snap id %" PRIu64,
2470 dst_snap_name.c_str(), snap_meta.id.val);
2471 return -EEXIST;
2472 }
2473 return 0;
2474 };
2475 r = image::snapshot::iterate(hctx, duplicate_name_lambda);
2476 if (r < 0) {
2477 return r;
2478 }
2479
2480 std::string src_snap_key;
2481 key_from_snap_id(src_snap_id, &src_snap_key);
2482 r = read_key(hctx, src_snap_key, &snap_meta);
2483 if (r == -ENOENT) {
2484 CLS_LOG(20, "cannot find existing snap with snap id = %" PRIu64,
2485 src_snap_id.val);
2486 return r;
2487 }
2488
2489 if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) !=
2490 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER) {
2491 // can only rename user snapshots
2492 return -EINVAL;
2493 }
2494
2495 snap_meta.name = dst_snap_name;
2496 r = image::snapshot::write(hctx, src_snap_key, std::move(snap_meta));
2497 if (r < 0) {
2498 return r;
2499 }
2500
2501 return 0;
2502 }
2503
2504 /**
2505 * Removes a snapshot from an rbd header.
2506 *
2507 * Input:
2508 * @param snap_id the id of the snapshot to remove (uint64_t)
2509 *
2510 * Output:
2511 * @returns 0 on success, negative error code on failure
2512 */
2513 int snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2514 {
2515 snapid_t snap_id;
2516
2517 try {
2518 auto iter = in->cbegin();
2519 decode(snap_id, iter);
2520 } catch (const ceph::buffer::error &err) {
2521 return -EINVAL;
2522 }
2523
2524 CLS_LOG(20, "snapshot_remove id=%llu", (unsigned long long)snap_id.val);
2525
2526 // check if the key exists. we can't rely on remove_key doing this for
2527 // us, since OMAPRMKEYS returns success if the key is not there.
2528 // bug or feature? sounds like a bug, since tmap did not have this
2529 // behavior, but cls_rgw may rely on it...
2530 cls_rbd_snap snap;
2531 string snapshot_key;
2532 key_from_snap_id(snap_id, &snapshot_key);
2533 int r = read_key(hctx, snapshot_key, &snap);
2534 if (r == -ENOENT) {
2535 return -ENOENT;
2536 }
2537
2538 if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED) {
2539 return -EBUSY;
2540 }
2541
2542 // snapshot is in-use by clone v2 child
2543 if (snap.child_count > 0) {
2544 return -EBUSY;
2545 }
2546
2547 r = remove_key(hctx, snapshot_key);
2548 if (r < 0) {
2549 return r;
2550 }
2551
2552 bool has_child_snaps = false;
2553 bool has_trash_snaps = false;
2554 auto remove_lambda = [snap_id, &has_child_snaps, &has_trash_snaps](
2555 const cls_rbd_snap& snap_meta) {
2556 if (snap_meta.id != snap_id) {
2557 if (snap_meta.parent.pool_id != -1 || snap_meta.parent_overlap) {
2558 has_child_snaps = true;
2559 }
2560
2561 if (cls::rbd::get_snap_namespace_type(snap_meta.snapshot_namespace) ==
2562 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
2563 has_trash_snaps = true;
2564 }
2565 }
2566 return 0;
2567 };
2568
2569 r = image::snapshot::iterate(hctx, remove_lambda);
2570 if (r < 0) {
2571 return r;
2572 }
2573
2574 cls_rbd_parent parent;
2575 r = read_key(hctx, "parent", &parent);
2576 if (r < 0 && r != -ENOENT) {
2577 return r;
2578 }
2579
2580 bool has_parent = (r >= 0 && parent.exists());
2581 bool is_head_child = (has_parent && parent.head_overlap);
2582 ceph_release_t require_osd_release = cls_get_required_osd_release(hctx);
2583 if (has_parent && !is_head_child && !has_child_snaps &&
2584 require_osd_release >= ceph_release_t::nautilus) {
2585 // remove the unused parent image spec
2586 r = remove_key(hctx, "parent");
2587 if (r < 0 && r != -ENOENT) {
2588 return r;
2589 }
2590 }
2591
2592 uint64_t op_features_mask = 0ULL;
2593 if (!has_child_snaps && !is_head_child) {
2594 // disable clone child op feature if no longer associated
2595 op_features_mask |= RBD_OPERATION_FEATURE_CLONE_CHILD;
2596 }
2597 if (!has_trash_snaps) {
2598 // remove the snap_trash op feature if not in-use by any other snapshots
2599 op_features_mask |= RBD_OPERATION_FEATURE_SNAP_TRASH;
2600 }
2601
2602 if (op_features_mask != 0ULL) {
2603 r = image::set_op_features(hctx, 0, op_features_mask);
2604 if (r < 0) {
2605 return r;
2606 }
2607 }
2608
2609 return 0;
2610 }
2611
2612 /**
2613 * Moves a snapshot to the trash namespace.
2614 *
2615 * Input:
2616 * @param snap_id the id of the snapshot to move to the trash (uint64_t)
2617 *
2618 * Output:
2619 * @returns 0 on success, negative error code on failure
2620 */
2621 int snapshot_trash_add(cls_method_context_t hctx, bufferlist *in,
2622 bufferlist *out)
2623 {
2624 snapid_t snap_id;
2625
2626 try {
2627 auto iter = in->cbegin();
2628 decode(snap_id, iter);
2629 } catch (const ceph::buffer::error &err) {
2630 return -EINVAL;
2631 }
2632
2633 CLS_LOG(20, "snapshot_trash_add id=%" PRIu64, snap_id.val);
2634
2635 cls_rbd_snap snap;
2636 std::string snapshot_key;
2637 key_from_snap_id(snap_id, &snapshot_key);
2638 int r = read_key(hctx, snapshot_key, &snap);
2639 if (r == -ENOENT) {
2640 return r;
2641 }
2642
2643 if (snap.protection_status != RBD_PROTECTION_STATUS_UNPROTECTED) {
2644 return -EBUSY;
2645 }
2646
2647 auto snap_type = cls::rbd::get_snap_namespace_type(snap.snapshot_namespace);
2648 if (snap_type == cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
2649 return -EEXIST;
2650 }
2651
2652 // add snap_trash feature bit if not already enabled
2653 r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_SNAP_TRASH,
2654 RBD_OPERATION_FEATURE_SNAP_TRASH);
2655 if (r < 0) {
2656 return r;
2657 }
2658
2659 snap.snapshot_namespace = cls::rbd::TrashSnapshotNamespace{snap_type,
2660 snap.name};
2661 uuid_d uuid_gen;
2662 uuid_gen.generate_random();
2663 snap.name = uuid_gen.to_string();
2664
2665 r = image::snapshot::write(hctx, snapshot_key, std::move(snap));
2666 if (r < 0) {
2667 return r;
2668 }
2669
2670 return 0;
2671 }
2672
2673 /**
2674 * Returns a uint64_t of all the features supported by this class.
2675 */
2676 int get_all_features(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2677 {
2678 uint64_t all_features = RBD_FEATURES_ALL;
2679 encode(all_features, *out);
2680 return 0;
2681 }
2682
2683 /**
2684 * "Copy up" data from the parent of a clone to the clone's object(s).
2685 * Used for implementing copy-on-write for a clone image. Client
2686 * will pass down a chunk of data that fits completely within one
2687 * clone block (one object), and is aligned (starts at beginning of block),
2688 * but may be shorter (for non-full parent blocks). The class method
2689 * can't know the object size to validate the requested length,
2690 * so it just writes the data as given if the child object doesn't
2691 * already exist, and returns success if it does.
2692 *
2693 * Input:
2694 * @param in bufferlist of data to write
2695 *
2696 * Output:
2697 * @returns 0 on success, or if block already exists in child
2698 * negative error code on other error
2699 */
2700
2701 int copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2702 {
2703 // check for existence; if child object exists, just return success
2704 if (cls_cxx_stat(hctx, NULL, NULL) == 0)
2705 return 0;
2706 CLS_LOG(20, "copyup: writing length %d\n", in->length());
2707 return cls_cxx_write(hctx, 0, in->length(), in);
2708 }
2709
2710 /**
2711 * Input:
2712 * @param extent_map map of extents to write
2713 * @param data bufferlist of data to write
2714 *
2715 * Output:
2716 * @returns 0 on success, or if block already exists in child
2717 * negative error code on other error
2718 */
2719
2720 int sparse_copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2721 {
2722 std::map<uint64_t, uint64_t> extent_map;
2723 bufferlist data;
2724
2725 try {
2726 auto iter = in->cbegin();
2727 decode(extent_map, iter);
2728 decode(data, iter);
2729 } catch (const ceph::buffer::error &err) {
2730 CLS_LOG(20, "sparse_copyup: invalid decode");
2731 return -EINVAL;
2732 }
2733
2734 int r = check_exists(hctx);
2735 if (r == 0) {
2736 return 0;
2737 }
2738
2739 if (extent_map.empty()) {
2740 CLS_LOG(20, "sparse_copyup: create empty object");
2741 r = cls_cxx_create(hctx, true);
2742 return r;
2743 }
2744
2745 uint64_t data_offset = 0;
2746 for (auto &it: extent_map) {
2747 auto off = it.first;
2748 auto len = it.second;
2749
2750 bufferlist tmpbl;
2751 try {
2752 tmpbl.substr_of(data, data_offset, len);
2753 } catch (const ceph::buffer::error &err) {
2754 CLS_LOG(20, "sparse_copyup: invalid data");
2755 return -EINVAL;
2756 }
2757 data_offset += len;
2758
2759 CLS_LOG(20, "sparse_copyup: writing extent %" PRIu64 "~%" PRIu64 "\n", off,
2760 len);
2761 int r = cls_cxx_write(hctx, off, len, &tmpbl);
2762 if (r < 0) {
2763 CLS_ERR("sparse_copyup: error writing extent %" PRIu64 "~%" PRIu64 ": %s",
2764 off, len, cpp_strerror(r).c_str());
2765 return r;
2766 }
2767 }
2768
2769 return 0;
2770 }
2771
2772 /************************ rbd_id object methods **************************/
2773
2774 /**
2775 * Input:
2776 * @param in ignored
2777 *
2778 * Output:
2779 * @param id the id stored in the object
2780 * @returns 0 on success, negative error code on failure
2781 */
2782 int get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2783 {
2784 uint64_t size;
2785 int r = cls_cxx_stat(hctx, &size, NULL);
2786 if (r < 0)
2787 return r;
2788
2789 if (size == 0)
2790 return -ENOENT;
2791
2792 bufferlist read_bl;
2793 r = cls_cxx_read(hctx, 0, size, &read_bl);
2794 if (r < 0) {
2795 CLS_ERR("get_id: could not read id: %s", cpp_strerror(r).c_str());
2796 return r;
2797 }
2798
2799 string id;
2800 try {
2801 auto iter = read_bl.cbegin();
2802 decode(id, iter);
2803 } catch (const ceph::buffer::error &err) {
2804 return -EIO;
2805 }
2806
2807 encode(id, *out);
2808 return 0;
2809 }
2810
2811 /**
2812 * Set the id of an image. The object must already exist.
2813 *
2814 * Input:
2815 * @param id the id of the image, as an alpha-numeric string
2816 *
2817 * Output:
2818 * @returns 0 on success, -EEXIST if the atomic create fails,
2819 * negative error code on other error
2820 */
2821 int set_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2822 {
2823 int r = check_exists(hctx);
2824 if (r < 0)
2825 return r;
2826
2827 string id;
2828 try {
2829 auto iter = in->cbegin();
2830 decode(id, iter);
2831 } catch (const ceph::buffer::error &err) {
2832 return -EINVAL;
2833 }
2834
2835 if (!is_valid_id(id)) {
2836 CLS_ERR("set_id: invalid id '%s'", id.c_str());
2837 return -EINVAL;
2838 }
2839
2840 uint64_t size;
2841 r = cls_cxx_stat(hctx, &size, NULL);
2842 if (r < 0)
2843 return r;
2844 if (size != 0)
2845 return -EEXIST;
2846
2847 CLS_LOG(20, "set_id: id=%s", id.c_str());
2848
2849 bufferlist write_bl;
2850 encode(id, write_bl);
2851 return cls_cxx_write(hctx, 0, write_bl.length(), &write_bl);
2852 }
2853
2854 /**
2855 * Update the access timestamp of an image
2856 *
2857 * Input:
2858 * @param none
2859 *
2860 * Output:
2861 * @returns 0 on success, negative error code on other error
2862 */
2863 int set_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2864 {
2865 int r = check_exists(hctx);
2866 if(r < 0)
2867 return r;
2868
2869 utime_t timestamp = ceph_clock_now();
2870 r = write_key(hctx, "access_timestamp", timestamp);
2871 if(r < 0) {
2872 CLS_ERR("error setting access_timestamp");
2873 return r;
2874 }
2875
2876 return 0;
2877 }
2878
2879 /**
2880 * Update the modify timestamp of an image
2881 *
2882 * Input:
2883 * @param none
2884 *
2885 * Output:
2886 * @returns 0 on success, negative error code on other error
2887 */
2888
2889 int set_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
2890 {
2891 int r = check_exists(hctx);
2892 if(r < 0)
2893 return r;
2894
2895 utime_t timestamp = ceph_clock_now();
2896 r = write_key(hctx, "modify_timestamp", timestamp);
2897 if(r < 0) {
2898 CLS_ERR("error setting modify_timestamp");
2899 return r;
2900 }
2901
2902 return 0;
2903 }
2904
2905
2906
2907 /*********************** methods for rbd_directory ***********************/
2908
2909 static const string dir_key_for_id(const string &id)
2910 {
2911 return RBD_DIR_ID_KEY_PREFIX + id;
2912 }
2913
2914 static const string dir_key_for_name(const string &name)
2915 {
2916 return RBD_DIR_NAME_KEY_PREFIX + name;
2917 }
2918
2919 static const string dir_name_from_key(const string &key)
2920 {
2921 return key.substr(strlen(RBD_DIR_NAME_KEY_PREFIX));
2922 }
2923
2924 static int dir_add_image_helper(cls_method_context_t hctx,
2925 const string &name, const string &id,
2926 bool check_for_unique_id)
2927 {
2928 if (!name.size() || !is_valid_id(id)) {
2929 CLS_ERR("dir_add_image_helper: invalid name '%s' or id '%s'",
2930 name.c_str(), id.c_str());
2931 return -EINVAL;
2932 }
2933
2934 CLS_LOG(20, "dir_add_image_helper name=%s id=%s", name.c_str(), id.c_str());
2935
2936 string tmp;
2937 string name_key = dir_key_for_name(name);
2938 string id_key = dir_key_for_id(id);
2939 int r = read_key(hctx, name_key, &tmp);
2940 if (r != -ENOENT) {
2941 CLS_LOG(10, "name already exists");
2942 return -EEXIST;
2943 }
2944 r = read_key(hctx, id_key, &tmp);
2945 if (r != -ENOENT && check_for_unique_id) {
2946 CLS_LOG(10, "id already exists");
2947 return -EBADF;
2948 }
2949 bufferlist id_bl, name_bl;
2950 encode(id, id_bl);
2951 encode(name, name_bl);
2952 map<string, bufferlist> omap_vals;
2953 omap_vals[name_key] = id_bl;
2954 omap_vals[id_key] = name_bl;
2955 return cls_cxx_map_set_vals(hctx, &omap_vals);
2956 }
2957
2958 static int dir_remove_image_helper(cls_method_context_t hctx,
2959 const string &name, const string &id)
2960 {
2961 CLS_LOG(20, "dir_remove_image_helper name=%s id=%s",
2962 name.c_str(), id.c_str());
2963
2964 string stored_name, stored_id;
2965 string name_key = dir_key_for_name(name);
2966 string id_key = dir_key_for_id(id);
2967 int r = read_key(hctx, name_key, &stored_id);
2968 if (r < 0) {
2969 if (r != -ENOENT)
2970 CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str());
2971 return r;
2972 }
2973 r = read_key(hctx, id_key, &stored_name);
2974 if (r < 0) {
2975 CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str());
2976 return r;
2977 }
2978
2979 // check if this op raced with a rename
2980 if (stored_name != name || stored_id != id) {
2981 CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
2982 stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str());
2983 return -ESTALE;
2984 }
2985
2986 r = cls_cxx_map_remove_key(hctx, name_key);
2987 if (r < 0) {
2988 CLS_ERR("error removing name: %s", cpp_strerror(r).c_str());
2989 return r;
2990 }
2991
2992 r = cls_cxx_map_remove_key(hctx, id_key);
2993 if (r < 0) {
2994 CLS_ERR("error removing id: %s", cpp_strerror(r).c_str());
2995 return r;
2996 }
2997
2998 return 0;
2999 }
3000
3001 /**
3002 * Rename an image in the directory, updating both indexes
3003 * atomically. This can't be done from the client calling
3004 * dir_add_image and dir_remove_image in one transaction because the
3005 * results of the first method are not visibale to later steps.
3006 *
3007 * Input:
3008 * @param src original name of the image
3009 * @param dest new name of the image
3010 * @param id the id of the image
3011 *
3012 * Output:
3013 * @returns -ESTALE if src and id do not map to each other
3014 * @returns -ENOENT if src or id are not in the directory
3015 * @returns -EEXIST if dest already exists
3016 * @returns 0 on success, negative error code on failure
3017 */
3018 int dir_rename_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3019 {
3020 string src, dest, id;
3021 try {
3022 auto iter = in->cbegin();
3023 decode(src, iter);
3024 decode(dest, iter);
3025 decode(id, iter);
3026 } catch (const ceph::buffer::error &err) {
3027 return -EINVAL;
3028 }
3029
3030 int r = dir_remove_image_helper(hctx, src, id);
3031 if (r < 0)
3032 return r;
3033 // ignore duplicate id because the result of
3034 // remove_image_helper is not visible yet
3035 return dir_add_image_helper(hctx, dest, id, false);
3036 }
3037
3038 /**
3039 * Get the id of an image given its name.
3040 *
3041 * Input:
3042 * @param name the name of the image
3043 *
3044 * Output:
3045 * @param id the id of the image
3046 * @returns 0 on success, negative error code on failure
3047 */
3048 int dir_get_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3049 {
3050 string name;
3051
3052 try {
3053 auto iter = in->cbegin();
3054 decode(name, iter);
3055 } catch (const ceph::buffer::error &err) {
3056 return -EINVAL;
3057 }
3058
3059 CLS_LOG(20, "dir_get_id: name=%s", name.c_str());
3060
3061 string id;
3062 int r = read_key(hctx, dir_key_for_name(name), &id);
3063 if (r < 0) {
3064 if (r != -ENOENT)
3065 CLS_ERR("error reading id for name '%s': %s", name.c_str(), cpp_strerror(r).c_str());
3066 return r;
3067 }
3068 encode(id, *out);
3069 return 0;
3070 }
3071
3072 /**
3073 * Get the name of an image given its id.
3074 *
3075 * Input:
3076 * @param id the id of the image
3077 *
3078 * Output:
3079 * @param name the name of the image
3080 * @returns 0 on success, negative error code on failure
3081 */
3082 int dir_get_name(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3083 {
3084 string id;
3085
3086 try {
3087 auto iter = in->cbegin();
3088 decode(id, iter);
3089 } catch (const ceph::buffer::error &err) {
3090 return -EINVAL;
3091 }
3092
3093 CLS_LOG(20, "dir_get_name: id=%s", id.c_str());
3094
3095 string name;
3096 int r = read_key(hctx, dir_key_for_id(id), &name);
3097 if (r < 0) {
3098 if (r != -ENOENT) {
3099 CLS_ERR("error reading name for id '%s': %s", id.c_str(),
3100 cpp_strerror(r).c_str());
3101 }
3102 return r;
3103 }
3104 encode(name, *out);
3105 return 0;
3106 }
3107
3108 /**
3109 * List the names and ids of the images in the directory, sorted by
3110 * name.
3111 *
3112 * Input:
3113 * @param start_after which name to begin listing after
3114 * (use the empty string to start at the beginning)
3115 * @param max_return the maximum number of names to list
3116 *
3117 * Output:
3118 * @param images map from name to id of up to max_return images
3119 * @returns 0 on success, negative error code on failure
3120 */
3121 int dir_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3122 {
3123 string start_after;
3124 uint64_t max_return;
3125
3126 try {
3127 auto iter = in->cbegin();
3128 decode(start_after, iter);
3129 decode(max_return, iter);
3130 } catch (const ceph::buffer::error &err) {
3131 return -EINVAL;
3132 }
3133
3134 int max_read = RBD_MAX_KEYS_READ;
3135 map<string, string> images;
3136 string last_read = dir_key_for_name(start_after);
3137 bool more = true;
3138
3139 while (more && images.size() < max_return) {
3140 map<string, bufferlist> vals;
3141 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
3142 int r = cls_cxx_map_get_vals(hctx, last_read, RBD_DIR_NAME_KEY_PREFIX,
3143 max_read, &vals, &more);
3144 if (r < 0) {
3145 if (r != -ENOENT) {
3146 CLS_ERR("error reading directory by name: %s", cpp_strerror(r).c_str());
3147 }
3148 return r;
3149 }
3150
3151 for (auto it = vals.begin(); it != vals.end(); ++it) {
3152 string id;
3153 auto iter = it->second.cbegin();
3154 try {
3155 decode(id, iter);
3156 } catch (const ceph::buffer::error &err) {
3157 CLS_ERR("could not decode id of image '%s'", it->first.c_str());
3158 return -EIO;
3159 }
3160 CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(it->first).c_str(), id.c_str());
3161 images[dir_name_from_key(it->first)] = id;
3162 if (images.size() >= max_return)
3163 break;
3164 }
3165 if (!vals.empty()) {
3166 last_read = dir_key_for_name(images.rbegin()->first);
3167 }
3168 }
3169
3170 encode(images, *out);
3171
3172 return 0;
3173 }
3174
3175 /**
3176 * Add an image to the rbd directory. Creates the directory object if
3177 * needed, and updates the index from id to name and name to id.
3178 *
3179 * Input:
3180 * @param name the name of the image
3181 * @param id the id of the image
3182 *
3183 * Output:
3184 * @returns -EEXIST if the image name is already in the directory
3185 * @returns -EBADF if the image id is already in the directory
3186 * @returns 0 on success, negative error code on failure
3187 */
3188 int dir_add_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3189 {
3190 int r = cls_cxx_create(hctx, false);
3191 if (r < 0) {
3192 CLS_ERR("could not create directory: %s", cpp_strerror(r).c_str());
3193 return r;
3194 }
3195
3196 string name, id;
3197 try {
3198 auto iter = in->cbegin();
3199 decode(name, iter);
3200 decode(id, iter);
3201 } catch (const ceph::buffer::error &err) {
3202 return -EINVAL;
3203 }
3204
3205 return dir_add_image_helper(hctx, name, id, true);
3206 }
3207
3208 /**
3209 * Remove an image from the rbd directory.
3210 *
3211 * Input:
3212 * @param name the name of the image
3213 * @param id the id of the image
3214 *
3215 * Output:
3216 * @returns -ESTALE if the name and id do not map to each other
3217 * @returns 0 on success, negative error code on failure
3218 */
3219 int dir_remove_image(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3220 {
3221 string name, id;
3222 try {
3223 auto iter = in->cbegin();
3224 decode(name, iter);
3225 decode(id, iter);
3226 } catch (const ceph::buffer::error &err) {
3227 return -EINVAL;
3228 }
3229
3230 return dir_remove_image_helper(hctx, name, id);
3231 }
3232
3233 /**
3234 * Verify the current state of the directory
3235 *
3236 * Input:
3237 * @param state the DirectoryState of the directory
3238 *
3239 * Output:
3240 * @returns -ENOENT if the state does not match
3241 * @returns 0 on success, negative error code on failure
3242 */
3243 int dir_state_assert(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3244 {
3245 cls::rbd::DirectoryState directory_state = cls::rbd::DIRECTORY_STATE_READY;
3246 try {
3247 auto iter = in->cbegin();
3248 decode(directory_state, iter);
3249 } catch (const ceph::buffer::error &err) {
3250 return -EINVAL;
3251 }
3252
3253 cls::rbd::DirectoryState on_disk_directory_state = directory_state;
3254 int r = read_key(hctx, "state", &on_disk_directory_state);
3255 if (r < 0) {
3256 return r;
3257 }
3258
3259 if (directory_state != on_disk_directory_state) {
3260 return -ENOENT;
3261 }
3262 return 0;
3263 }
3264
3265 /**
3266 * Set the current state of the directory
3267 *
3268 * Input:
3269 * @param state the DirectoryState of the directory
3270 *
3271 * Output:
3272 * @returns -ENOENT if the state does not match
3273 * @returns 0 on success, negative error code on failure
3274 */
3275 int dir_state_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3276 {
3277 cls::rbd::DirectoryState directory_state;
3278 try {
3279 auto iter = in->cbegin();
3280 decode(directory_state, iter);
3281 } catch (const ceph::buffer::error &err) {
3282 return -EINVAL;
3283 }
3284
3285 int r = check_exists(hctx);
3286 if (r < 0 && r != -ENOENT) {
3287 return r;
3288 }
3289
3290 switch (directory_state) {
3291 case cls::rbd::DIRECTORY_STATE_READY:
3292 break;
3293 case cls::rbd::DIRECTORY_STATE_ADD_DISABLED:
3294 {
3295 if (r == -ENOENT) {
3296 return r;
3297 }
3298
3299 // verify that the directory is empty
3300 std::map<std::string, bufferlist> vals;
3301 bool more;
3302 r = cls_cxx_map_get_vals(hctx, RBD_DIR_NAME_KEY_PREFIX,
3303 RBD_DIR_NAME_KEY_PREFIX, 1, &vals, &more);
3304 if (r < 0) {
3305 return r;
3306 } else if (!vals.empty()) {
3307 return -EBUSY;
3308 }
3309 }
3310 break;
3311 default:
3312 return -EINVAL;
3313 }
3314
3315 r = write_key(hctx, "state", directory_state);
3316 if (r < 0) {
3317 return r;
3318 }
3319
3320 return 0;
3321 }
3322
3323 int object_map_read(cls_method_context_t hctx, BitVector<2> &object_map)
3324 {
3325 uint64_t size;
3326 int r = cls_cxx_stat(hctx, &size, NULL);
3327 if (r < 0) {
3328 return r;
3329 }
3330 if (size == 0) {
3331 return -ENOENT;
3332 }
3333
3334 bufferlist bl;
3335 r = cls_cxx_read(hctx, 0, size, &bl);
3336 if (r < 0) {
3337 return r;
3338 }
3339
3340 try {
3341 auto iter = bl.cbegin();
3342 decode(object_map, iter);
3343 } catch (const ceph::buffer::error &err) {
3344 CLS_ERR("failed to decode object map: %s", err.what());
3345 return -EINVAL;
3346 }
3347 return 0;
3348 }
3349
3350 /**
3351 * Load an rbd image's object map
3352 *
3353 * Input:
3354 * none
3355 *
3356 * Output:
3357 * @param object map bit vector
3358 * @returns 0 on success, negative error code on failure
3359 */
3360 int object_map_load(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3361 {
3362 BitVector<2> object_map;
3363 int r = object_map_read(hctx, object_map);
3364 if (r < 0) {
3365 return r;
3366 }
3367
3368 object_map.set_crc_enabled(false);
3369 encode(object_map, *out);
3370 return 0;
3371 }
3372
3373 /**
3374 * Save an rbd image's object map
3375 *
3376 * Input:
3377 * @param object map bit vector
3378 *
3379 * Output:
3380 * @returns 0 on success, negative error code on failure
3381 */
3382 int object_map_save(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3383 {
3384 BitVector<2> object_map;
3385 try {
3386 auto iter = in->cbegin();
3387 decode(object_map, iter);
3388 } catch (const ceph::buffer::error &err) {
3389 return -EINVAL;
3390 }
3391
3392 object_map.set_crc_enabled(true);
3393
3394 bufferlist bl;
3395 encode(object_map, bl);
3396 CLS_LOG(20, "object_map_save: object size=%" PRIu64 ", byte size=%u",
3397 object_map.size(), bl.length());
3398 return cls_cxx_write_full(hctx, &bl);
3399 }
3400
3401 /**
3402 * Resize an rbd image's object map
3403 *
3404 * Input:
3405 * @param object_count the max number of objects in the image
3406 * @param default_state the default state of newly created objects
3407 *
3408 * Output:
3409 * @returns 0 on success, negative error code on failure
3410 */
3411 int object_map_resize(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3412 {
3413 uint64_t object_count;
3414 uint8_t default_state;
3415 try {
3416 auto iter = in->cbegin();
3417 decode(object_count, iter);
3418 decode(default_state, iter);
3419 } catch (const ceph::buffer::error &err) {
3420 return -EINVAL;
3421 }
3422
3423 // protect against excessive memory requirements
3424 if (object_count > cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT) {
3425 CLS_ERR("object map too large: %" PRIu64, object_count);
3426 return -EINVAL;
3427 }
3428
3429 BitVector<2> object_map;
3430 int r = object_map_read(hctx, object_map);
3431 if ((r < 0) && (r != -ENOENT)) {
3432 return r;
3433 }
3434
3435 size_t orig_object_map_size = object_map.size();
3436 if (object_count < orig_object_map_size) {
3437 auto it = object_map.begin() + object_count;
3438 auto end_it = object_map.end() ;
3439 uint64_t i = object_count;
3440 for (; it != end_it; ++it, ++i) {
3441 if (*it != default_state) {
3442 CLS_ERR("object map indicates object still exists: %" PRIu64, i);
3443 return -ESTALE;
3444 }
3445 }
3446 object_map.resize(object_count);
3447 } else if (object_count > orig_object_map_size) {
3448 object_map.resize(object_count);
3449 auto it = object_map.begin() + orig_object_map_size;
3450 auto end_it = object_map.end();
3451 for (; it != end_it; ++it) {
3452 *it = default_state;
3453 }
3454 }
3455
3456 bufferlist map;
3457 encode(object_map, map);
3458 CLS_LOG(20, "object_map_resize: object size=%" PRIu64 ", byte size=%u",
3459 object_count, map.length());
3460 return cls_cxx_write_full(hctx, &map);
3461 }
3462
3463 /**
3464 * Update an rbd image's object map
3465 *
3466 * Input:
3467 * @param start_object_no the start object iterator
3468 * @param end_object_no the end object iterator
3469 * @param new_object_state the new object state
3470 * @param current_object_state optional current object state filter
3471 *
3472 * Output:
3473 * @returns 0 on success, negative error code on failure
3474 */
3475 int object_map_update(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3476 {
3477 uint64_t start_object_no;
3478 uint64_t end_object_no;
3479 uint8_t new_object_state;
3480 boost::optional<uint8_t> current_object_state;
3481 try {
3482 auto iter = in->cbegin();
3483 decode(start_object_no, iter);
3484 decode(end_object_no, iter);
3485 decode(new_object_state, iter);
3486 decode(current_object_state, iter);
3487 } catch (const ceph::buffer::error &err) {
3488 CLS_ERR("failed to decode message");
3489 return -EINVAL;
3490 }
3491
3492 uint64_t size;
3493 int r = cls_cxx_stat(hctx, &size, NULL);
3494 if (r < 0) {
3495 return r;
3496 }
3497
3498 BitVector<2> object_map;
3499 bufferlist header_bl;
3500 r = cls_cxx_read2(hctx, 0, object_map.get_header_length(), &header_bl,
3501 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3502 if (r < 0) {
3503 CLS_ERR("object map header read failed");
3504 return r;
3505 }
3506
3507 try {
3508 auto it = header_bl.cbegin();
3509 object_map.decode_header(it);
3510 } catch (const ceph::buffer::error &err) {
3511 CLS_ERR("failed to decode object map header: %s", err.what());
3512 return -EINVAL;
3513 }
3514
3515 uint64_t object_byte_offset;
3516 uint64_t byte_length;
3517 object_map.get_header_crc_extents(&object_byte_offset, &byte_length);
3518
3519 bufferlist footer_bl;
3520 r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &footer_bl,
3521 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3522 if (r < 0) {
3523 CLS_ERR("object map footer read header CRC failed");
3524 return r;
3525 }
3526
3527 try {
3528 auto it = footer_bl.cbegin();
3529 object_map.decode_header_crc(it);
3530 } catch (const ceph::buffer::error &err) {
3531 CLS_ERR("failed to decode object map header CRC: %s", err.what());
3532 }
3533
3534 if (start_object_no >= end_object_no || end_object_no > object_map.size()) {
3535 return -ERANGE;
3536 }
3537
3538 uint64_t object_count = end_object_no - start_object_no;
3539 object_map.get_data_crcs_extents(start_object_no, object_count,
3540 &object_byte_offset, &byte_length);
3541 const auto footer_object_offset = object_byte_offset;
3542
3543 footer_bl.clear();
3544 r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &footer_bl,
3545 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3546 if (r < 0) {
3547 CLS_ERR("object map footer read data CRCs failed");
3548 return r;
3549 }
3550
3551 try {
3552 auto it = footer_bl.cbegin();
3553 object_map.decode_data_crcs(it, start_object_no);
3554 } catch (const ceph::buffer::error &err) {
3555 CLS_ERR("failed to decode object map data CRCs: %s", err.what());
3556 }
3557
3558 uint64_t data_byte_offset;
3559 object_map.get_data_extents(start_object_no, object_count,
3560 &data_byte_offset, &object_byte_offset,
3561 &byte_length);
3562
3563 bufferlist data_bl;
3564 r = cls_cxx_read2(hctx, object_byte_offset, byte_length, &data_bl,
3565 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3566 if (r < 0) {
3567 CLS_ERR("object map data read failed");
3568 return r;
3569 }
3570
3571 try {
3572 auto it = data_bl.cbegin();
3573 object_map.decode_data(it, data_byte_offset);
3574 } catch (const ceph::buffer::error &err) {
3575 CLS_ERR("failed to decode data chunk [%" PRIu64 "]: %s",
3576 data_byte_offset, err.what());
3577 return -EINVAL;
3578 }
3579
3580 bool updated = false;
3581 auto it = object_map.begin() + start_object_no;
3582 auto end_it = object_map.begin() + end_object_no;
3583 for (; it != end_it; ++it) {
3584 uint8_t state = *it;
3585 if ((!current_object_state || state == *current_object_state ||
3586 (*current_object_state == OBJECT_EXISTS &&
3587 state == OBJECT_EXISTS_CLEAN)) && state != new_object_state) {
3588 *it = new_object_state;
3589 updated = true;
3590 }
3591 }
3592
3593 if (updated) {
3594 CLS_LOG(20, "object_map_update: %" PRIu64 "~%" PRIu64 " -> %" PRIu64,
3595 data_byte_offset, byte_length, object_byte_offset);
3596
3597 bufferlist data_bl;
3598 object_map.encode_data(data_bl, data_byte_offset, byte_length);
3599 r = cls_cxx_write2(hctx, object_byte_offset, data_bl.length(), &data_bl,
3600 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3601 if (r < 0) {
3602 CLS_ERR("failed to write object map header: %s", cpp_strerror(r).c_str());
3603 return r;
3604 }
3605
3606 footer_bl.clear();
3607 object_map.encode_data_crcs(footer_bl, start_object_no, object_count);
3608 r = cls_cxx_write2(hctx, footer_object_offset, footer_bl.length(),
3609 &footer_bl, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED);
3610 if (r < 0) {
3611 CLS_ERR("failed to write object map footer: %s", cpp_strerror(r).c_str());
3612 return r;
3613 }
3614 } else {
3615 CLS_LOG(20, "object_map_update: no update necessary");
3616 }
3617
3618 return 0;
3619 }
3620
3621 /**
3622 * Mark all _EXISTS objects as _EXISTS_CLEAN so future writes to the
3623 * image HEAD can be tracked.
3624 *
3625 * Input:
3626 * none
3627 *
3628 * Output:
3629 * @returns 0 on success, negative error code on failure
3630 */
3631 int object_map_snap_add(cls_method_context_t hctx, bufferlist *in,
3632 bufferlist *out)
3633 {
3634 BitVector<2> object_map;
3635 int r = object_map_read(hctx, object_map);
3636 if (r < 0) {
3637 return r;
3638 }
3639
3640 bool updated = false;
3641 auto it = object_map.begin();
3642 auto end_it = object_map.end();
3643 for (; it != end_it; ++it) {
3644 if (*it == OBJECT_EXISTS) {
3645 *it = OBJECT_EXISTS_CLEAN;
3646 updated = true;
3647 }
3648 }
3649
3650 if (updated) {
3651 bufferlist bl;
3652 encode(object_map, bl);
3653 r = cls_cxx_write_full(hctx, &bl);
3654 }
3655 return r;
3656 }
3657
3658 /**
3659 * Mark all _EXISTS_CLEAN objects as _EXISTS in the current object map
3660 * if the provided snapshot object map object is marked as _EXISTS.
3661 *
3662 * Input:
3663 * @param snapshot object map bit vector
3664 *
3665 * Output:
3666 * @returns 0 on success, negative error code on failure
3667 */
3668 int object_map_snap_remove(cls_method_context_t hctx, bufferlist *in,
3669 bufferlist *out)
3670 {
3671 BitVector<2> src_object_map;
3672 try {
3673 auto iter = in->cbegin();
3674 decode(src_object_map, iter);
3675 } catch (const ceph::buffer::error &err) {
3676 return -EINVAL;
3677 }
3678
3679 BitVector<2> dst_object_map;
3680 int r = object_map_read(hctx, dst_object_map);
3681 if (r < 0) {
3682 return r;
3683 }
3684
3685 bool updated = false;
3686 auto src_it = src_object_map.begin();
3687 auto dst_it = dst_object_map.begin();
3688 auto dst_it_end = dst_object_map.end();
3689 uint64_t i = 0;
3690 for (; dst_it != dst_it_end; ++dst_it) {
3691 if (*dst_it == OBJECT_EXISTS_CLEAN &&
3692 (i >= src_object_map.size() || *src_it == OBJECT_EXISTS)) {
3693 *dst_it = OBJECT_EXISTS;
3694 updated = true;
3695 }
3696 if (i < src_object_map.size())
3697 ++src_it;
3698 ++i;
3699 }
3700
3701 if (updated) {
3702 bufferlist bl;
3703 encode(dst_object_map, bl);
3704 r = cls_cxx_write_full(hctx, &bl);
3705 }
3706 return r;
3707 }
3708
3709 static const string metadata_key_for_name(const string &name)
3710 {
3711 return RBD_METADATA_KEY_PREFIX + name;
3712 }
3713
3714 static const string metadata_name_from_key(const string &key)
3715 {
3716 return key.substr(strlen(RBD_METADATA_KEY_PREFIX));
3717 }
3718
3719 /**
3720 * Input:
3721 * @param start_after which name to begin listing after
3722 * (use the empty string to start at the beginning)
3723 * @param max_return the maximum number of names to list
3724
3725 * Output:
3726 * @param value
3727 * @returns 0 on success, negative error code on failure
3728 */
3729 int metadata_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3730 {
3731 string start_after;
3732 uint64_t max_return;
3733
3734 try {
3735 auto iter = in->cbegin();
3736 decode(start_after, iter);
3737 decode(max_return, iter);
3738 } catch (const ceph::buffer::error &err) {
3739 return -EINVAL;
3740 }
3741
3742 // TODO remove implicit support for zero during the N-release
3743 if (max_return == 0) {
3744 max_return = RBD_MAX_KEYS_READ;
3745 }
3746
3747 map<string, bufferlist> data;
3748 string last_read = metadata_key_for_name(start_after);
3749 bool more = true;
3750
3751 while (more && data.size() < max_return) {
3752 map<string, bufferlist> raw_data;
3753 int max_read = std::min<uint64_t>(RBD_MAX_KEYS_READ, max_return - data.size());
3754 int r = cls_cxx_map_get_vals(hctx, last_read, RBD_METADATA_KEY_PREFIX,
3755 max_read, &raw_data, &more);
3756 if (r < 0) {
3757 if (r != -ENOENT) {
3758 CLS_ERR("failed to read the vals off of disk: %s",
3759 cpp_strerror(r).c_str());
3760 }
3761 return r;
3762 }
3763
3764 for (auto& kv : raw_data) {
3765 data[metadata_name_from_key(kv.first)].swap(kv.second);
3766 }
3767
3768 if (!raw_data.empty()) {
3769 last_read = raw_data.rbegin()->first;
3770 }
3771 }
3772
3773 encode(data, *out);
3774 return 0;
3775 }
3776
3777 /**
3778 * Input:
3779 * @param data <map(key, value)>
3780 *
3781 * Output:
3782 * @returns 0 on success, negative error code on failure
3783 */
3784 int metadata_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3785 {
3786 map<string, bufferlist> data, raw_data;
3787
3788 auto iter = in->cbegin();
3789 try {
3790 decode(data, iter);
3791 } catch (const ceph::buffer::error &err) {
3792 return -EINVAL;
3793 }
3794
3795 for (auto it = data.begin(); it != data.end(); ++it) {
3796 CLS_LOG(20, "metadata_set key=%s value=%.*s", it->first.c_str(),
3797 it->second.length(), it->second.c_str());
3798 raw_data[metadata_key_for_name(it->first)].swap(it->second);
3799 }
3800 int r = cls_cxx_map_set_vals(hctx, &raw_data);
3801 if (r < 0) {
3802 CLS_ERR("error writing metadata: %s", cpp_strerror(r).c_str());
3803 return r;
3804 }
3805
3806 return 0;
3807 }
3808
3809 /**
3810 * Input:
3811 * @param key
3812 *
3813 * Output:
3814 * @returns 0 on success, negative error code on failure
3815 */
3816 int metadata_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3817 {
3818 string key;
3819
3820 auto iter = in->cbegin();
3821 try {
3822 decode(key, iter);
3823 } catch (const ceph::buffer::error &err) {
3824 return -EINVAL;
3825 }
3826
3827 CLS_LOG(20, "metadata_remove key=%s", key.c_str());
3828
3829 int r = cls_cxx_map_remove_key(hctx, metadata_key_for_name(key));
3830 if (r < 0) {
3831 CLS_ERR("error removing metadata: %s", cpp_strerror(r).c_str());
3832 return r;
3833 }
3834
3835 return 0;
3836 }
3837
3838 /**
3839 * Input:
3840 * @param key
3841 *
3842 * Output:
3843 * @param metadata value associated with the key
3844 * @returns 0 on success, negative error code on failure
3845 */
3846 int metadata_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3847 {
3848 string key;
3849 bufferlist value;
3850
3851 auto iter = in->cbegin();
3852 try {
3853 decode(key, iter);
3854 } catch (const ceph::buffer::error &err) {
3855 return -EINVAL;
3856 }
3857
3858 CLS_LOG(20, "metadata_get key=%s", key.c_str());
3859
3860 int r = cls_cxx_map_get_val(hctx, metadata_key_for_name(key), &value);
3861 if (r < 0) {
3862 if (r != -ENOENT)
3863 CLS_ERR("error getting metadata: %s", cpp_strerror(r).c_str());
3864 return r;
3865 }
3866
3867 encode(value, *out);
3868 return 0;
3869 }
3870
3871 int snapshot_get_limit(cls_method_context_t hctx, bufferlist *in,
3872 bufferlist *out)
3873 {
3874 uint64_t snap_limit;
3875 int r = read_key(hctx, "snap_limit", &snap_limit);
3876 if (r == -ENOENT) {
3877 snap_limit = UINT64_MAX;
3878 } else if (r < 0) {
3879 CLS_ERR("error retrieving snapshot limit: %s", cpp_strerror(r).c_str());
3880 return r;
3881 }
3882
3883 CLS_LOG(20, "read snapshot limit %" PRIu64, snap_limit);
3884 encode(snap_limit, *out);
3885
3886 return 0;
3887 }
3888
3889 int snapshot_set_limit(cls_method_context_t hctx, bufferlist *in,
3890 bufferlist *out)
3891 {
3892 int rc;
3893 uint64_t new_limit;
3894 bufferlist bl;
3895 size_t snap_count = 0;
3896
3897 try {
3898 auto iter = in->cbegin();
3899 decode(new_limit, iter);
3900 } catch (const ceph::buffer::error &err) {
3901 return -EINVAL;
3902 }
3903
3904 if (new_limit == UINT64_MAX) {
3905 CLS_LOG(20, "remove snapshot limit\n");
3906 rc = cls_cxx_map_remove_key(hctx, "snap_limit");
3907 return rc;
3908 }
3909
3910 //try to read header as v1 format
3911 rc = snap_read_header(hctx, bl);
3912
3913 // error when reading header
3914 if (rc < 0 && rc != -EINVAL) {
3915 return rc;
3916 } else if (rc >= 0) {
3917 // success, the image is v1 format
3918 struct rbd_obj_header_ondisk *header;
3919 header = (struct rbd_obj_header_ondisk *)bl.c_str();
3920 snap_count = header->snap_count;
3921 } else {
3922 // else, the image is v2 format
3923 int max_read = RBD_MAX_KEYS_READ;
3924 string last_read = RBD_SNAP_KEY_PREFIX;
3925 bool more;
3926
3927 do {
3928 set<string> keys;
3929 rc = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more);
3930 if (rc < 0) {
3931 CLS_ERR("error retrieving snapshots: %s", cpp_strerror(rc).c_str());
3932 return rc;
3933 }
3934 for (auto& key : keys) {
3935 if (key.find(RBD_SNAP_KEY_PREFIX) != 0)
3936 break;
3937 snap_count++;
3938 }
3939 if (!keys.empty())
3940 last_read = *(keys.rbegin());
3941 } while (more);
3942 }
3943
3944 if (new_limit < snap_count) {
3945 rc = -ERANGE;
3946 CLS_LOG(10, "snapshot limit is less than the number of snapshots.\n");
3947 } else {
3948 CLS_LOG(20, "set snapshot limit to %" PRIu64 "\n", new_limit);
3949 bl.clear();
3950 encode(new_limit, bl);
3951 rc = cls_cxx_map_set_val(hctx, "snap_limit", &bl);
3952 }
3953
3954 return rc;
3955 }
3956
3957
3958 /**
3959 * Input:
3960 * @param snap id (uint64_t) parent snapshot id
3961 * @param child spec (cls::rbd::ChildImageSpec) child image
3962 *
3963 * Output:
3964 * @returns 0 on success, negative error code on failure
3965 */
3966 int child_attach(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
3967 {
3968 uint64_t snap_id;
3969 cls::rbd::ChildImageSpec child_image;
3970 try {
3971 auto it = in->cbegin();
3972 decode(snap_id, it);
3973 decode(child_image, it);
3974 } catch (const ceph::buffer::error &err) {
3975 return -EINVAL;
3976 }
3977
3978 CLS_LOG(20, "child_attach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", "
3979 "child_image_id=%s", snap_id, child_image.pool_id,
3980 child_image.image_id.c_str());
3981
3982 cls_rbd_snap snap;
3983 std::string snapshot_key;
3984 key_from_snap_id(snap_id, &snapshot_key);
3985 int r = read_key(hctx, snapshot_key, &snap);
3986 if (r < 0) {
3987 return r;
3988 }
3989
3990 if (cls::rbd::get_snap_namespace_type(snap.snapshot_namespace) ==
3991 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH) {
3992 // cannot attach to a deleted snapshot
3993 return -ENOENT;
3994 }
3995
3996 auto children_key = image::snap_children_key_from_snap_id(snap_id);
3997 cls::rbd::ChildImageSpecs child_images;
3998 r = read_key(hctx, children_key, &child_images);
3999 if (r < 0 && r != -ENOENT) {
4000 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str());
4001 return r;
4002 }
4003
4004 auto it = child_images.insert(child_image);
4005 if (!it.second) {
4006 // child already attached to the snapshot
4007 return -EEXIST;
4008 }
4009
4010 r = write_key(hctx, children_key, child_images);
4011 if (r < 0) {
4012 CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str());
4013 return r;
4014 }
4015
4016 ++snap.child_count;
4017 r = image::snapshot::write(hctx, snapshot_key, std::move(snap));
4018 if (r < 0) {
4019 return r;
4020 }
4021
4022 r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_CLONE_PARENT,
4023 RBD_OPERATION_FEATURE_CLONE_PARENT);
4024 if (r < 0) {
4025 return r;
4026 }
4027
4028 return 0;
4029 }
4030
4031 /**
4032 * Input:
4033 * @param snap id (uint64_t) parent snapshot id
4034 * @param child spec (cls::rbd::ChildImageSpec) child image
4035 *
4036 * Output:
4037 * @returns 0 on success, negative error code on failure
4038 */
4039 int child_detach(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4040 {
4041 uint64_t snap_id;
4042 cls::rbd::ChildImageSpec child_image;
4043 try {
4044 auto it = in->cbegin();
4045 decode(snap_id, it);
4046 decode(child_image, it);
4047 } catch (const ceph::buffer::error &err) {
4048 return -EINVAL;
4049 }
4050
4051 CLS_LOG(20, "child_detach snap_id=%" PRIu64 ", child_pool_id=%" PRIi64 ", "
4052 "child_image_id=%s", snap_id, child_image.pool_id,
4053 child_image.image_id.c_str());
4054
4055 cls_rbd_snap snap;
4056 std::string snapshot_key;
4057 key_from_snap_id(snap_id, &snapshot_key);
4058 int r = read_key(hctx, snapshot_key, &snap);
4059 if (r < 0) {
4060 return r;
4061 }
4062
4063 auto children_key = image::snap_children_key_from_snap_id(snap_id);
4064 cls::rbd::ChildImageSpecs child_images;
4065 r = read_key(hctx, children_key, &child_images);
4066 if (r < 0 && r != -ENOENT) {
4067 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str());
4068 return r;
4069 }
4070
4071 if (snap.child_count != child_images.size()) {
4072 // children and reference count don't match
4073 CLS_ERR("children reference count mismatch: %" PRIu64, snap_id);
4074 return -EINVAL;
4075 }
4076
4077 if (child_images.erase(child_image) == 0) {
4078 // child not attached to the snapshot
4079 return -ENOENT;
4080 }
4081
4082 if (child_images.empty()) {
4083 r = remove_key(hctx, children_key);
4084 } else {
4085 r = write_key(hctx, children_key, child_images);
4086 if (r < 0) {
4087 CLS_ERR("error writing snapshot children: %s", cpp_strerror(r).c_str());
4088 return r;
4089 }
4090 }
4091
4092 --snap.child_count;
4093 r = image::snapshot::write(hctx, snapshot_key, std::move(snap));
4094 if (r < 0) {
4095 return r;
4096 }
4097
4098 if (snap.child_count == 0) {
4099 auto clone_in_use_lambda = [snap_id](const cls_rbd_snap& snap_meta) {
4100 if (snap_meta.id != snap_id && snap_meta.child_count > 0) {
4101 return -EEXIST;
4102 }
4103 return 0;
4104 };
4105
4106 r = image::snapshot::iterate(hctx, clone_in_use_lambda);
4107 if (r < 0 && r != -EEXIST) {
4108 return r;
4109 }
4110
4111 if (r != -EEXIST) {
4112 // remove the clone_v2 op feature if not in-use by any other snapshots
4113 r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_CLONE_PARENT);
4114 if (r < 0) {
4115 return r;
4116 }
4117 }
4118 }
4119
4120 return 0;
4121 }
4122
4123 /**
4124 * Input:
4125 * @param snap id (uint64_t) parent snapshot id
4126 *
4127 * Output:
4128 * @param (cls::rbd::ChildImageSpecs) child images
4129 * @returns 0 on success, negative error code on failure
4130 */
4131 int children_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4132 {
4133 uint64_t snap_id;
4134 try {
4135 auto it = in->cbegin();
4136 decode(snap_id, it);
4137 } catch (const ceph::buffer::error &err) {
4138 return -EINVAL;
4139 }
4140
4141 CLS_LOG(20, "children_list snap_id=%" PRIu64, snap_id);
4142
4143 cls_rbd_snap snap;
4144 std::string snapshot_key;
4145 key_from_snap_id(snap_id, &snapshot_key);
4146 int r = read_key(hctx, snapshot_key, &snap);
4147 if (r < 0) {
4148 return r;
4149 }
4150
4151 auto children_key = image::snap_children_key_from_snap_id(snap_id);
4152 cls::rbd::ChildImageSpecs child_images;
4153 r = read_key(hctx, children_key, &child_images);
4154 if (r == -ENOENT) {
4155 return r;
4156 } else if (r < 0) {
4157 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r).c_str());
4158 return r;
4159 }
4160
4161 encode(child_images, *out);
4162 return 0;
4163 }
4164
4165 /**
4166 * Set image migration.
4167 *
4168 * Input:
4169 * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
4170 *
4171 * Output:
4172 *
4173 * @returns 0 on success, negative error code on failure
4174 */
4175 int migration_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
4176 cls::rbd::MigrationSpec migration_spec;
4177 try {
4178 auto it = in->cbegin();
4179 decode(migration_spec, it);
4180 } catch (const ceph::buffer::error &err) {
4181 return -EINVAL;
4182 }
4183
4184 int r = image::set_migration(hctx, migration_spec, true);
4185 if (r < 0) {
4186 return r;
4187 }
4188
4189 return 0;
4190 }
4191
4192 /**
4193 * Set image migration state.
4194 *
4195 * Input:
4196 * @param state (cls::rbd::MigrationState) migration state
4197 * @param description (std::string) migration state description
4198 *
4199 * Output:
4200 *
4201 * @returns 0 on success, negative error code on failure
4202 */
4203 int migration_set_state(cls_method_context_t hctx, bufferlist *in,
4204 bufferlist *out) {
4205 cls::rbd::MigrationState state;
4206 std::string description;
4207 try {
4208 auto it = in->cbegin();
4209 decode(state, it);
4210 decode(description, it);
4211 } catch (const ceph::buffer::error &err) {
4212 return -EINVAL;
4213 }
4214
4215 cls::rbd::MigrationSpec migration_spec;
4216 int r = image::read_migration(hctx, &migration_spec);
4217 if (r < 0) {
4218 return r;
4219 }
4220
4221 migration_spec.state = state;
4222 migration_spec.state_description = description;
4223
4224 r = image::set_migration(hctx, migration_spec, false);
4225 if (r < 0) {
4226 return r;
4227 }
4228
4229 return 0;
4230 }
4231
4232 /**
4233 * Get image migration spec.
4234 *
4235 * Input:
4236 *
4237 * Output:
4238 * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
4239 *
4240 * @returns 0 on success, negative error code on failure
4241 */
4242 int migration_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
4243 cls::rbd::MigrationSpec migration_spec;
4244 int r = image::read_migration(hctx, &migration_spec);
4245 if (r < 0) {
4246 return r;
4247 }
4248
4249 encode(migration_spec, *out);
4250
4251 return 0;
4252 }
4253
4254 /**
4255 * Remove image migration spec.
4256 *
4257 * Input:
4258 *
4259 * Output:
4260 *
4261 * @returns 0 on success, negative error code on failure
4262 */
4263 int migration_remove(cls_method_context_t hctx, bufferlist *in,
4264 bufferlist *out) {
4265 int r = image::remove_migration(hctx);
4266 if (r < 0) {
4267 return r;
4268 }
4269
4270 return 0;
4271 }
4272
4273 /**
4274 * Ensure writer snapc state
4275 *
4276 * Input:
4277 * @param snap id (uint64_t) snap context sequence id
4278 * @param state (cls::rbd::AssertSnapcSeqState) snap context state
4279 *
4280 * Output:
4281 * @returns -ERANGE if assertion fails
4282 * @returns 0 on success, negative error code on failure
4283 */
4284 int assert_snapc_seq(cls_method_context_t hctx, bufferlist *in,
4285 bufferlist *out)
4286 {
4287 uint64_t snapc_seq;
4288 cls::rbd::AssertSnapcSeqState state;
4289 try {
4290 auto it = in->cbegin();
4291 decode(snapc_seq, it);
4292 decode(state, it);
4293 } catch (const ceph::buffer::error &err) {
4294 return -EINVAL;
4295 }
4296
4297 uint64_t snapset_seq;
4298 int r = cls_get_snapset_seq(hctx, &snapset_seq);
4299 if (r < 0 && r != -ENOENT) {
4300 return r;
4301 }
4302
4303 switch (state) {
4304 case cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ:
4305 return (r == -ENOENT || snapc_seq > snapset_seq) ? 0 : -ERANGE;
4306 case cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ:
4307 return (r == -ENOENT || snapc_seq > snapset_seq) ? -ERANGE : 0;
4308 default:
4309 return -EOPNOTSUPP;
4310 }
4311 }
4312
4313 /****************************** Old format *******************************/
4314
4315 int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4316 {
4317 bufferlist bl;
4318 struct rbd_obj_header_ondisk *header;
4319 int rc = snap_read_header(hctx, bl);
4320 if (rc < 0)
4321 return rc;
4322
4323 header = (struct rbd_obj_header_ondisk *)bl.c_str();
4324 bufferptr p(header->snap_names_len);
4325 char *buf = (char *)header;
4326 char *name = buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk);
4327 char *end = name + header->snap_names_len;
4328 memcpy(p.c_str(),
4329 buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk),
4330 header->snap_names_len);
4331
4332 encode(header->snap_seq, *out);
4333 encode(header->snap_count, *out);
4334
4335 for (unsigned i = 0; i < header->snap_count; i++) {
4336 string s = name;
4337 encode(header->snaps[i].id, *out);
4338 encode(header->snaps[i].image_size, *out);
4339 encode(s, *out);
4340
4341 name += strlen(name) + 1;
4342 if (name > end)
4343 return -EIO;
4344 }
4345
4346 return 0;
4347 }
4348
4349 int old_snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4350 {
4351 bufferlist bl;
4352 struct rbd_obj_header_ondisk *header;
4353 bufferlist newbl;
4354 bufferptr header_bp(sizeof(*header));
4355 struct rbd_obj_snap_ondisk *new_snaps;
4356
4357 int rc = snap_read_header(hctx, bl);
4358 if (rc < 0)
4359 return rc;
4360
4361 header = (struct rbd_obj_header_ondisk *)bl.c_str();
4362
4363 int snaps_id_ofs = sizeof(*header);
4364 int names_ofs = snaps_id_ofs + sizeof(*new_snaps) * header->snap_count;
4365 const char *snap_name;
4366 const char *snap_names = ((char *)header) + names_ofs;
4367 const char *end = snap_names + header->snap_names_len;
4368 auto iter = in->cbegin();
4369 string s;
4370 uint64_t snap_id;
4371
4372 try {
4373 decode(s, iter);
4374 decode(snap_id, iter);
4375 } catch (const ceph::buffer::error &err) {
4376 return -EINVAL;
4377 }
4378 snap_name = s.c_str();
4379
4380 if (header->snap_seq > snap_id)
4381 return -ESTALE;
4382
4383 uint64_t snap_limit;
4384 rc = read_key(hctx, "snap_limit", &snap_limit);
4385 if (rc == -ENOENT) {
4386 snap_limit = UINT64_MAX;
4387 } else if (rc < 0) {
4388 return rc;
4389 }
4390
4391 if (header->snap_count >= snap_limit)
4392 return -EDQUOT;
4393
4394 const char *cur_snap_name;
4395 for (cur_snap_name = snap_names; cur_snap_name < end; cur_snap_name += strlen(cur_snap_name) + 1) {
4396 if (strncmp(cur_snap_name, snap_name, end - cur_snap_name) == 0)
4397 return -EEXIST;
4398 }
4399 if (cur_snap_name > end)
4400 return -EIO;
4401
4402 int snap_name_len = strlen(snap_name);
4403
4404 bufferptr new_names_bp(header->snap_names_len + snap_name_len + 1);
4405 bufferptr new_snaps_bp(sizeof(*new_snaps) * (header->snap_count + 1));
4406
4407 /* copy snap names and append to new snap name */
4408 char *new_snap_names = new_names_bp.c_str();
4409 strcpy(new_snap_names, snap_name);
4410 memcpy(new_snap_names + snap_name_len + 1, snap_names, header->snap_names_len);
4411
4412 /* append new snap id */
4413 new_snaps = (struct rbd_obj_snap_ondisk *)new_snaps_bp.c_str();
4414 memcpy(new_snaps + 1, header->snaps, sizeof(*new_snaps) * header->snap_count);
4415
4416 header->snap_count = header->snap_count + 1;
4417 header->snap_names_len = header->snap_names_len + snap_name_len + 1;
4418 header->snap_seq = snap_id;
4419
4420 new_snaps[0].id = snap_id;
4421 new_snaps[0].image_size = header->image_size;
4422
4423 memcpy(header_bp.c_str(), header, sizeof(*header));
4424
4425 newbl.push_back(header_bp);
4426 newbl.push_back(new_snaps_bp);
4427 newbl.push_back(new_names_bp);
4428
4429 rc = cls_cxx_write_full(hctx, &newbl);
4430 if (rc < 0)
4431 return rc;
4432
4433 return 0;
4434 }
4435
4436 int old_snapshot_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4437 {
4438 bufferlist bl;
4439 struct rbd_obj_header_ondisk *header;
4440 bufferlist newbl;
4441 bufferptr header_bp(sizeof(*header));
4442
4443 int rc = snap_read_header(hctx, bl);
4444 if (rc < 0)
4445 return rc;
4446
4447 header = (struct rbd_obj_header_ondisk *)bl.c_str();
4448
4449 int snaps_id_ofs = sizeof(*header);
4450 int names_ofs = snaps_id_ofs + sizeof(struct rbd_obj_snap_ondisk) * header->snap_count;
4451 const char *snap_name;
4452 const char *snap_names = ((char *)header) + names_ofs;
4453 const char *orig_names = snap_names;
4454 const char *end = snap_names + header->snap_names_len;
4455 auto iter = in->cbegin();
4456 string s;
4457 unsigned i;
4458 bool found = false;
4459 struct rbd_obj_snap_ondisk snap;
4460
4461 try {
4462 decode(s, iter);
4463 } catch (const ceph::buffer::error &err) {
4464 return -EINVAL;
4465 }
4466 snap_name = s.c_str();
4467
4468 for (i = 0; snap_names < end; i++) {
4469 if (strcmp(snap_names, snap_name) == 0) {
4470 snap = header->snaps[i];
4471 found = true;
4472 break;
4473 }
4474 snap_names += strlen(snap_names) + 1;
4475 }
4476 if (!found) {
4477 CLS_ERR("couldn't find snap %s\n", snap_name);
4478 return -ENOENT;
4479 }
4480
4481 header->snap_names_len = header->snap_names_len - (s.length() + 1);
4482 header->snap_count = header->snap_count - 1;
4483
4484 bufferptr new_names_bp(header->snap_names_len);
4485 bufferptr new_snaps_bp(sizeof(header->snaps[0]) * header->snap_count);
4486
4487 memcpy(header_bp.c_str(), header, sizeof(*header));
4488 newbl.push_back(header_bp);
4489
4490 if (header->snap_count) {
4491 int snaps_len = 0;
4492 int names_len = 0;
4493 CLS_LOG(20, "i=%u\n", i);
4494 if (i > 0) {
4495 snaps_len = sizeof(header->snaps[0]) * i;
4496 names_len = snap_names - orig_names;
4497 memcpy(new_snaps_bp.c_str(), header->snaps, snaps_len);
4498 memcpy(new_names_bp.c_str(), orig_names, names_len);
4499 }
4500 snap_names += s.length() + 1;
4501
4502 if (i < header->snap_count) {
4503 memcpy(new_snaps_bp.c_str() + snaps_len,
4504 header->snaps + i + 1,
4505 sizeof(header->snaps[0]) * (header->snap_count - i));
4506 memcpy(new_names_bp.c_str() + names_len, snap_names , end - snap_names);
4507 }
4508 newbl.push_back(new_snaps_bp);
4509 newbl.push_back(new_names_bp);
4510 }
4511
4512 rc = cls_cxx_write_full(hctx, &newbl);
4513 if (rc < 0)
4514 return rc;
4515
4516 return 0;
4517 }
4518
4519 /**
4520 * rename snapshot of old format.
4521 *
4522 * Input:
4523 * @param src_snap_id old snap id of the snapshot (snapid_t)
4524 * @param dst_snap_name new name of the snapshot (string)
4525 *
4526 * Output:
4527 * @returns 0 on success, negative error code on failure.
4528 */
4529 int old_snapshot_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
4530 {
4531 bufferlist bl;
4532 struct rbd_obj_header_ondisk *header;
4533 bufferlist newbl;
4534 bufferptr header_bp(sizeof(*header));
4535 snapid_t src_snap_id;
4536 const char *dst_snap_name;
4537 string dst;
4538
4539 int rc = snap_read_header(hctx, bl);
4540 if (rc < 0)
4541 return rc;
4542
4543 header = (struct rbd_obj_header_ondisk *)bl.c_str();
4544
4545 int snaps_id_ofs = sizeof(*header);
4546 int names_ofs = snaps_id_ofs + sizeof(rbd_obj_snap_ondisk) * header->snap_count;
4547 const char *snap_names = ((char *)header) + names_ofs;
4548 const char *orig_names = snap_names;
4549 const char *end = snap_names + header->snap_names_len;
4550 auto iter = in->cbegin();
4551 unsigned i;
4552 bool found = false;
4553
4554 try {
4555 decode(src_snap_id, iter);
4556 decode(dst, iter);
4557 } catch (const ceph::buffer::error &err) {
4558 return -EINVAL;
4559 }
4560 dst_snap_name = dst.c_str();
4561
4562 const char *cur_snap_name;
4563 for (cur_snap_name = snap_names; cur_snap_name < end;
4564 cur_snap_name += strlen(cur_snap_name) + 1) {
4565 if (strcmp(cur_snap_name, dst_snap_name) == 0)
4566 return -EEXIST;
4567 }
4568 if (cur_snap_name > end)
4569 return -EIO;
4570 for (i = 0; i < header->snap_count; i++) {
4571 if (src_snap_id == header->snaps[i].id) {
4572 found = true;
4573 break;
4574 }
4575 snap_names += strlen(snap_names) + 1;
4576 }
4577 if (!found) {
4578 CLS_ERR("couldn't find snap %llu\n", (unsigned long long)src_snap_id.val);
4579 return -ENOENT;
4580 }
4581
4582 CLS_LOG(20, "rename snap with snap id %llu to dest name %s", (unsigned long long)src_snap_id.val, dst_snap_name);
4583 header->snap_names_len = header->snap_names_len - strlen(snap_names) + dst.length();
4584
4585 bufferptr new_names_bp(header->snap_names_len);
4586 bufferptr new_snaps_bp(sizeof(header->snaps[0]) * header->snap_count);
4587
4588 if (header->snap_count) {
4589 int names_len = 0;
4590 CLS_LOG(20, "i=%u\n", i);
4591 if (i > 0) {
4592 names_len = snap_names - orig_names;
4593 memcpy(new_names_bp.c_str(), orig_names, names_len);
4594 }
4595 strcpy(new_names_bp.c_str() + names_len, dst_snap_name);
4596 names_len += strlen(dst_snap_name) + 1;
4597 snap_names += strlen(snap_names) + 1;
4598 if (i < header->snap_count) {
4599 memcpy(new_names_bp.c_str() + names_len, snap_names , end - snap_names);
4600 }
4601 memcpy(new_snaps_bp.c_str(), header->snaps, sizeof(header->snaps[0]) * header->snap_count);
4602 }
4603
4604 memcpy(header_bp.c_str(), header, sizeof(*header));
4605 newbl.push_back(header_bp);
4606 newbl.push_back(new_snaps_bp);
4607 newbl.push_back(new_names_bp);
4608
4609 rc = cls_cxx_write_full(hctx, &newbl);
4610 if (rc < 0)
4611 return rc;
4612 return 0;
4613 }
4614
4615
4616 namespace mirror {
4617
4618 static const std::string UUID("mirror_uuid");
4619 static const std::string MODE("mirror_mode");
4620 static const std::string PEER_KEY_PREFIX("mirror_peer_");
4621 static const std::string IMAGE_KEY_PREFIX("image_");
4622 static const std::string GLOBAL_KEY_PREFIX("global_");
4623 static const std::string STATUS_GLOBAL_KEY_PREFIX("status_global_");
4624 static const std::string REMOTE_STATUS_GLOBAL_KEY_PREFIX("remote_status_global_");
4625 static const std::string INSTANCE_KEY_PREFIX("instance_");
4626 static const std::string MIRROR_IMAGE_MAP_KEY_PREFIX("image_map_");
4627
4628 std::string peer_key(const std::string &uuid) {
4629 return PEER_KEY_PREFIX + uuid;
4630 }
4631
4632 std::string image_key(const string &image_id) {
4633 return IMAGE_KEY_PREFIX + image_id;
4634 }
4635
4636 std::string global_key(const string &global_id) {
4637 return GLOBAL_KEY_PREFIX + global_id;
4638 }
4639
4640 std::string remote_status_global_key(const std::string& global_id,
4641 const std::string& mirror_uuid) {
4642 return REMOTE_STATUS_GLOBAL_KEY_PREFIX + global_id + "_" + mirror_uuid;
4643 }
4644
4645 std::string status_global_key(const std::string& global_id,
4646 const std::string& mirror_uuid) {
4647 if (mirror_uuid == cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID) {
4648 return STATUS_GLOBAL_KEY_PREFIX + global_id;
4649 } else {
4650 return remote_status_global_key(global_id, mirror_uuid);
4651 }
4652 }
4653
4654 std::string instance_key(const string &instance_id) {
4655 return INSTANCE_KEY_PREFIX + instance_id;
4656 }
4657
4658 std::string mirror_image_map_key(const string& global_image_id) {
4659 return MIRROR_IMAGE_MAP_KEY_PREFIX + global_image_id;
4660 }
4661
4662 int uuid_get(cls_method_context_t hctx, std::string *mirror_uuid) {
4663 bufferlist mirror_uuid_bl;
4664 int r = cls_cxx_map_get_val(hctx, mirror::UUID, &mirror_uuid_bl);
4665 if (r < 0) {
4666 if (r != -ENOENT) {
4667 CLS_ERR("error reading mirror uuid: %s", cpp_strerror(r).c_str());
4668 }
4669 return r;
4670 }
4671
4672 *mirror_uuid = std::string(mirror_uuid_bl.c_str(), mirror_uuid_bl.length());
4673 return 0;
4674 }
4675
4676 int list_watchers(cls_method_context_t hctx,
4677 std::set<entity_inst_t> *entities) {
4678 obj_list_watch_response_t watchers;
4679 int r = cls_cxx_list_watchers(hctx, &watchers);
4680 if (r < 0 && r != -ENOENT) {
4681 CLS_ERR("error listing watchers: '%s'", cpp_strerror(r).c_str());
4682 return r;
4683 }
4684
4685 entities->clear();
4686 for (auto &w : watchers.entries) {
4687 entity_inst_t entity_inst{w.name, w.addr};
4688 cls::rbd::sanitize_entity_inst(&entity_inst);
4689
4690 entities->insert(entity_inst);
4691 }
4692 return 0;
4693 }
4694
4695 int read_peers(cls_method_context_t hctx,
4696 std::vector<cls::rbd::MirrorPeer> *peers) {
4697 std::string last_read = PEER_KEY_PREFIX;
4698 int max_read = RBD_MAX_KEYS_READ;
4699 bool more = true;
4700 while (more) {
4701 std::map<std::string, bufferlist> vals;
4702 int r = cls_cxx_map_get_vals(hctx, last_read, PEER_KEY_PREFIX.c_str(),
4703 max_read, &vals, &more);
4704 if (r < 0) {
4705 if (r != -ENOENT) {
4706 CLS_ERR("error reading peers: %s", cpp_strerror(r).c_str());
4707 }
4708 return r;
4709 }
4710
4711 for (auto &it : vals) {
4712 try {
4713 auto bl_it = it.second.cbegin();
4714 cls::rbd::MirrorPeer peer;
4715 decode(peer, bl_it);
4716 peers->push_back(peer);
4717 } catch (const ceph::buffer::error &err) {
4718 CLS_ERR("could not decode peer '%s'", it.first.c_str());
4719 return -EIO;
4720 }
4721 }
4722
4723 if (!vals.empty()) {
4724 last_read = vals.rbegin()->first;
4725 }
4726 }
4727 return 0;
4728 }
4729
4730 int read_peer(cls_method_context_t hctx, const std::string &id,
4731 cls::rbd::MirrorPeer *peer) {
4732 bufferlist bl;
4733 int r = cls_cxx_map_get_val(hctx, peer_key(id), &bl);
4734 if (r < 0) {
4735 CLS_ERR("error reading peer '%s': %s", id.c_str(),
4736 cpp_strerror(r).c_str());
4737 return r;
4738 }
4739
4740 try {
4741 auto bl_it = bl.cbegin();
4742 decode(*peer, bl_it);
4743 } catch (const ceph::buffer::error &err) {
4744 CLS_ERR("could not decode peer '%s'", id.c_str());
4745 return -EIO;
4746 }
4747 return 0;
4748 }
4749
4750 int write_peer(cls_method_context_t hctx, const cls::rbd::MirrorPeer &peer) {
4751 bufferlist bl;
4752 encode(peer, bl);
4753
4754 int r = cls_cxx_map_set_val(hctx, peer_key(peer.uuid), &bl);
4755 if (r < 0) {
4756 CLS_ERR("error writing peer '%s': %s", peer.uuid.c_str(),
4757 cpp_strerror(r).c_str());
4758 return r;
4759 }
4760 return 0;
4761 }
4762
4763 int check_mirroring_enabled(cls_method_context_t hctx) {
4764 uint32_t mirror_mode_decode;
4765 int r = read_key(hctx, mirror::MODE, &mirror_mode_decode);
4766 if (r < 0 && r != -ENOENT) {
4767 return r;
4768 } else if (r == -ENOENT ||
4769 mirror_mode_decode == cls::rbd::MIRROR_MODE_DISABLED) {
4770 CLS_ERR("mirroring must be enabled on the pool");
4771 return -EINVAL;
4772 }
4773
4774 return 0;
4775 }
4776
4777 int peer_ping(cls_method_context_t hctx, const std::string& site_name,
4778 const std::string& mirror_uuid) {
4779 int r = check_mirroring_enabled(hctx);
4780 if (r < 0) {
4781 return r;
4782 }
4783
4784 if (site_name.empty() || mirror_uuid.empty()) {
4785 return -EINVAL;
4786 }
4787
4788 std::vector<cls::rbd::MirrorPeer> peers;
4789 r = read_peers(hctx, &peers);
4790 if (r < 0 && r != -ENOENT) {
4791 return r;
4792 }
4793
4794
4795 cls::rbd::MirrorPeer mirror_peer;
4796 auto site_it = std::find_if(peers.begin(), peers.end(),
4797 [&site_name](auto& peer) {
4798 return (peer.site_name == site_name);
4799 });
4800
4801 auto mirror_uuid_it = peers.end();
4802 if (site_it == peers.end() ||
4803 (!site_it->mirror_uuid.empty() && site_it->mirror_uuid != mirror_uuid)) {
4804 // search for existing peer w/ same mirror_uuid
4805 mirror_uuid_it = std::find_if(peers.begin(), peers.end(),
4806 [&mirror_uuid](auto& peer) {
4807 return (peer.mirror_uuid == mirror_uuid);
4808 });
4809 }
4810
4811 auto it = peers.end();
4812 if (site_it != peers.end() && mirror_uuid_it != peers.end()) {
4813 // implies two peers -- match by mirror_uuid but don't update site name
4814 it = mirror_uuid_it;
4815 } else if (mirror_uuid_it != peers.end()) {
4816 // implies site name has been updated in remote
4817 mirror_uuid_it->site_name = site_name;
4818 it = mirror_uuid_it;
4819 } else if (site_it != peers.end()) {
4820 // implies empty mirror_uuid in peer
4821 site_it->mirror_uuid = mirror_uuid;
4822 it = site_it;
4823 } else {
4824 CLS_LOG(10, "auto-generating new TX-only peer: %s", site_name.c_str());
4825
4826 uuid_d uuid_gen;
4827 while (true) {
4828 uuid_gen.generate_random();
4829 mirror_peer.uuid = uuid_gen.to_string();
4830
4831 bufferlist bl;
4832 r = cls_cxx_map_get_val(hctx, peer_key(mirror_peer.uuid), &bl);
4833 if (r == -ENOENT) {
4834 break;
4835 } else if (r < 0) {
4836 CLS_ERR("failed to retrieve mirror peer: %s", cpp_strerror(r).c_str());
4837 return r;
4838 }
4839 }
4840
4841 mirror_peer.mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_TX;
4842 mirror_peer.site_name = site_name;
4843 mirror_peer.mirror_uuid = mirror_uuid;
4844 }
4845
4846 if (it != peers.end()) {
4847 mirror_peer = *it;
4848
4849 if (mirror_peer.mirror_peer_direction ==
4850 cls::rbd::MIRROR_PEER_DIRECTION_RX) {
4851 CLS_LOG(10, "switching to RX/TX peer: %s", site_name.c_str());
4852 mirror_peer.mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_RX_TX;
4853 }
4854 }
4855
4856 mirror_peer.last_seen = ceph_clock_now();
4857
4858 if (!mirror_peer.is_valid()) {
4859 CLS_ERR("attempting to update invalid peer: %s", site_name.c_str());
4860 return -EINVAL;
4861 }
4862
4863 r = write_peer(hctx, mirror_peer);
4864 if (r < 0) {
4865 return r;
4866 }
4867
4868 return 0;
4869 }
4870
4871 int peer_add(cls_method_context_t hctx, cls::rbd::MirrorPeer mirror_peer) {
4872 int r = check_mirroring_enabled(hctx);
4873 if (r < 0) {
4874 return r;
4875 }
4876
4877 if (!mirror_peer.is_valid()) {
4878 CLS_ERR("mirror peer is not valid");
4879 return -EINVAL;
4880 }
4881
4882 std::string mirror_uuid;
4883 r = uuid_get(hctx, &mirror_uuid);
4884 if (r < 0) {
4885 CLS_ERR("error retrieving mirroring uuid: %s", cpp_strerror(r).c_str());
4886 return r;
4887 } else if (mirror_peer.uuid == mirror_uuid) {
4888 CLS_ERR("peer uuid '%s' matches pool mirroring uuid",
4889 mirror_uuid.c_str());
4890 return -EINVAL;
4891 } else if (mirror_peer.mirror_peer_direction ==
4892 cls::rbd::MIRROR_PEER_DIRECTION_TX) {
4893 CLS_ERR("peer uuid '%s' cannot use TX-only direction",
4894 mirror_peer.uuid.c_str());
4895 return -EINVAL;
4896 }
4897
4898 std::vector<cls::rbd::MirrorPeer> peers;
4899 r = read_peers(hctx, &peers);
4900 if (r < 0 && r != -ENOENT) {
4901 return r;
4902 }
4903
4904 for (auto const &peer : peers) {
4905 if (peer.uuid == mirror_peer.uuid) {
4906 CLS_ERR("peer uuid '%s' already exists",
4907 peer.uuid.c_str());
4908 return -ESTALE;
4909 } else if (peer.site_name == mirror_peer.site_name) {
4910 CLS_ERR("peer site name '%s' already exists",
4911 peer.site_name.c_str());
4912 return -EEXIST;
4913 } else if (!mirror_peer.mirror_uuid.empty() &&
4914 peer.mirror_uuid == mirror_peer.mirror_uuid) {
4915 CLS_ERR("peer mirror uuid '%s' already exists",
4916 peer.mirror_uuid.c_str());
4917 return -EEXIST;
4918 }
4919 }
4920
4921 r = write_peer(hctx, mirror_peer);
4922 if (r < 0) {
4923 return r;
4924 }
4925 return 0;
4926 }
4927
4928 int peer_remove(cls_method_context_t hctx, const std::string& uuid) {
4929 int r = cls_cxx_map_remove_key(hctx, peer_key(uuid));
4930 if (r < 0 && r != -ENOENT) {
4931 CLS_ERR("error removing peer: %s", cpp_strerror(r).c_str());
4932 return r;
4933 }
4934 return 0;
4935 }
4936
4937 int image_get(cls_method_context_t hctx, const string &image_id,
4938 cls::rbd::MirrorImage *mirror_image) {
4939 bufferlist bl;
4940 int r = cls_cxx_map_get_val(hctx, image_key(image_id), &bl);
4941 if (r < 0) {
4942 if (r != -ENOENT) {
4943 CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(),
4944 cpp_strerror(r).c_str());
4945 }
4946 return r;
4947 }
4948
4949 try {
4950 auto it = bl.cbegin();
4951 decode(*mirror_image, it);
4952 } catch (const ceph::buffer::error &err) {
4953 CLS_ERR("could not decode mirrored image '%s'", image_id.c_str());
4954 return -EIO;
4955 }
4956
4957 return 0;
4958 }
4959
4960 int image_set(cls_method_context_t hctx, const string &image_id,
4961 const cls::rbd::MirrorImage &mirror_image) {
4962 bufferlist bl;
4963 encode(mirror_image, bl);
4964
4965 cls::rbd::MirrorImage existing_mirror_image;
4966 int r = image_get(hctx, image_id, &existing_mirror_image);
4967 if (r == -ENOENT) {
4968 // make sure global id doesn't already exist
4969 std::string global_id_key = global_key(mirror_image.global_image_id);
4970 std::string image_id;
4971 r = read_key(hctx, global_id_key, &image_id);
4972 if (r >= 0) {
4973 return -EEXIST;
4974 } else if (r != -ENOENT) {
4975 CLS_ERR("error reading global image id: '%s': '%s'", image_id.c_str(),
4976 cpp_strerror(r).c_str());
4977 return r;
4978 }
4979
4980 // make sure this was not a race for disabling
4981 if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
4982 CLS_ERR("image '%s' is already disabled", image_id.c_str());
4983 return r;
4984 }
4985 } else if (r < 0) {
4986 CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(),
4987 cpp_strerror(r).c_str());
4988 return r;
4989 } else if (existing_mirror_image.global_image_id !=
4990 mirror_image.global_image_id) {
4991 // cannot change the global id
4992 return -EINVAL;
4993 }
4994
4995 r = cls_cxx_map_set_val(hctx, image_key(image_id), &bl);
4996 if (r < 0) {
4997 CLS_ERR("error adding mirrored image '%s': %s", image_id.c_str(),
4998 cpp_strerror(r).c_str());
4999 return r;
5000 }
5001
5002 bufferlist image_id_bl;
5003 encode(image_id, image_id_bl);
5004 r = cls_cxx_map_set_val(hctx, global_key(mirror_image.global_image_id),
5005 &image_id_bl);
5006 if (r < 0) {
5007 CLS_ERR("error adding global id for image '%s': %s", image_id.c_str(),
5008 cpp_strerror(r).c_str());
5009 return r;
5010 }
5011 return 0;
5012 }
5013
5014 int image_status_remove(cls_method_context_t hctx,
5015 const string &global_image_id);
5016
5017 int image_remove(cls_method_context_t hctx, const string &image_id) {
5018 bufferlist bl;
5019 cls::rbd::MirrorImage mirror_image;
5020 int r = image_get(hctx, image_id, &mirror_image);
5021 if (r < 0) {
5022 if (r != -ENOENT) {
5023 CLS_ERR("error reading mirrored image '%s': '%s'", image_id.c_str(),
5024 cpp_strerror(r).c_str());
5025 }
5026 return r;
5027 }
5028
5029 if (mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_DISABLING) {
5030 return -EBUSY;
5031 }
5032
5033 r = cls_cxx_map_remove_key(hctx, image_key(image_id));
5034 if (r < 0) {
5035 CLS_ERR("error removing mirrored image '%s': %s", image_id.c_str(),
5036 cpp_strerror(r).c_str());
5037 return r;
5038 }
5039
5040 r = cls_cxx_map_remove_key(hctx, global_key(mirror_image.global_image_id));
5041 if (r < 0 && r != -ENOENT) {
5042 CLS_ERR("error removing global id for image '%s': %s", image_id.c_str(),
5043 cpp_strerror(r).c_str());
5044 return r;
5045 }
5046
5047 r = image_status_remove(hctx, mirror_image.global_image_id);
5048 if (r < 0) {
5049 return r;
5050 }
5051
5052 return 0;
5053 }
5054
5055 int image_status_set(cls_method_context_t hctx, const string &global_image_id,
5056 const cls::rbd::MirrorImageSiteStatus &status) {
5057 cls::rbd::MirrorImageSiteStatusOnDisk ondisk_status(status);
5058 ondisk_status.mirror_uuid = ""; // mirror_uuid stored in key
5059 ondisk_status.up = false;
5060 ondisk_status.last_update = ceph_clock_now();
5061
5062 std::string global_id_key = global_key(global_image_id);
5063 std::string image_id;
5064 int r = read_key(hctx, global_id_key, &image_id);
5065 if (r < 0) {
5066 return 0;
5067 }
5068 cls::rbd::MirrorImage mirror_image;
5069 r = image_get(hctx, image_id, &mirror_image);
5070 if (r < 0) {
5071 return 0;
5072 }
5073 if (mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
5074 return 0;
5075 }
5076
5077 r = cls_get_request_origin(hctx, &ondisk_status.origin);
5078 ceph_assert(r == 0);
5079
5080 bufferlist bl;
5081 encode(ondisk_status, bl, cls_get_features(hctx));
5082
5083 r = cls_cxx_map_set_val(hctx, status_global_key(global_image_id,
5084 status.mirror_uuid), &bl);
5085 if (r < 0) {
5086 CLS_ERR("error setting status for mirrored image, global id '%s', "
5087 "site '%s': %s", global_image_id.c_str(),
5088 status.mirror_uuid.c_str(),
5089 cpp_strerror(r).c_str());
5090 return r;
5091 }
5092 return 0;
5093 }
5094
5095 int get_remote_image_status_mirror_uuids(cls_method_context_t hctx,
5096 const std::string& global_image_id,
5097 std::set<std::string>* mirror_uuids) {
5098 std::string filter = remote_status_global_key(global_image_id, "");
5099 std::string last_read = filter;
5100 int max_read = 4; // we don't expect lots of peers
5101 bool more = true;
5102
5103 do {
5104 std::set<std::string> keys;
5105 int r = cls_cxx_map_get_keys(hctx, last_read, max_read, &keys, &more);
5106 if (r < 0) {
5107 return r;
5108 }
5109
5110 for (auto& key : keys) {
5111 if (!boost::starts_with(key, filter)) {
5112 more = false;
5113 break;
5114 }
5115
5116 mirror_uuids->insert(key.substr(filter.length()));
5117 }
5118
5119 if (!keys.empty()) {
5120 last_read = *keys.rbegin();
5121 }
5122 } while (more);
5123
5124 return 0;
5125 }
5126
5127 int image_status_remove(cls_method_context_t hctx,
5128 const string &global_image_id) {
5129 // remove all local/remote image statuses
5130 std::set<std::string> mirror_uuids;
5131 int r = get_remote_image_status_mirror_uuids(hctx, global_image_id,
5132 &mirror_uuids);
5133 if (r < 0 && r != -ENOENT) {
5134 return r;
5135 }
5136
5137 mirror_uuids.insert(cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID);
5138 for (auto& mirror_uuid : mirror_uuids) {
5139 CLS_LOG(20, "removing status object for mirror_uuid %s",
5140 mirror_uuid.c_str());
5141 auto key = status_global_key(global_image_id, mirror_uuid);
5142 r = cls_cxx_map_remove_key(hctx, key);
5143 if (r < 0 && r != -ENOENT) {
5144 CLS_ERR("error removing stale status for key '%s': %s",
5145 key.c_str(), cpp_strerror(r).c_str());
5146 return r;
5147 }
5148 }
5149
5150 return 0;
5151 }
5152
5153 int image_status_get(cls_method_context_t hctx, const string &global_image_id,
5154 const std::string& mirror_uuid, const bufferlist& bl,
5155 const std::set<entity_inst_t> &watchers,
5156 cls::rbd::MirrorImageStatus* status) {
5157 cls::rbd::MirrorImageSiteStatusOnDisk ondisk_status;
5158 try {
5159 auto it = bl.cbegin();
5160 decode(ondisk_status, it);
5161 } catch (const ceph::buffer::error &err) {
5162 CLS_ERR("could not decode status for mirrored image, global id '%s', "
5163 "site '%s'",
5164 global_image_id.c_str(), mirror_uuid.c_str());
5165 return -EIO;
5166 }
5167
5168 auto site_status = static_cast<cls::rbd::MirrorImageSiteStatus>(
5169 ondisk_status);
5170 site_status.up = (watchers.find(ondisk_status.origin) != watchers.end());
5171 site_status.mirror_uuid = mirror_uuid;
5172 status->mirror_image_site_statuses.push_back(site_status);
5173 return 0;
5174 }
5175
5176 int image_status_get_local(cls_method_context_t hctx,
5177 const string &global_image_id,
5178 const std::set<entity_inst_t> &watchers,
5179 cls::rbd::MirrorImageStatus *status) {
5180 bufferlist bl;
5181 int r = cls_cxx_map_get_val(
5182 hctx, status_global_key(global_image_id,
5183 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID),
5184 &bl);
5185 if (r == -ENOENT) {
5186 return 0;
5187 } else if (r < 0) {
5188 CLS_ERR("error reading status for mirrored image, global id '%s', "
5189 "site '%s': '%s'",
5190 global_image_id.c_str(),
5191 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID.c_str(),
5192 cpp_strerror(r).c_str());
5193 return r;
5194 }
5195
5196 return image_status_get(hctx, global_image_id,
5197 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID,
5198 bl, watchers, status);
5199 }
5200
5201 int image_status_get_remote(cls_method_context_t hctx,
5202 const string &global_image_id,
5203 const std::set<entity_inst_t> &watchers,
5204 cls::rbd::MirrorImageStatus *status) {
5205 std::string filter = remote_status_global_key(global_image_id, "");
5206 std::string last_read = filter;
5207 int max_read = RBD_MAX_KEYS_READ;
5208 bool more = true;
5209
5210 do {
5211 std::map<std::string, bufferlist> vals;
5212 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
5213 int r = cls_cxx_map_get_vals(hctx, last_read, filter, max_read, &vals,
5214 &more);
5215 if (r == -ENOENT) {
5216 return 0;
5217 } else if (r < 0) {
5218 return r;
5219 }
5220
5221 for (auto& it : vals) {
5222 auto mirror_uuid = it.first.substr(filter.length());
5223 CLS_LOG(20, "mirror_uuid = '%s'", mirror_uuid.c_str());
5224 r = image_status_get(hctx, global_image_id, mirror_uuid, it.second,
5225 watchers, status);
5226 if (r < 0) {
5227 return r;
5228 }
5229 }
5230
5231 if (!vals.empty()) {
5232 last_read = vals.rbegin()->first;
5233 }
5234 } while (more);
5235
5236 return 0;
5237 }
5238
5239 int image_status_get(cls_method_context_t hctx, const string &global_image_id,
5240 const std::set<entity_inst_t> &watchers,
5241 cls::rbd::MirrorImageStatus *status) {
5242 status->mirror_image_site_statuses.clear();
5243
5244 // collect local site status
5245 int r = image_status_get_local(hctx, global_image_id, watchers, status);
5246 if (r < 0) {
5247 return r;
5248 }
5249
5250 // collect remote site status (TX to peer)
5251 r = image_status_get_remote(hctx, global_image_id, watchers, status);
5252 if (r < 0) {
5253 return r;
5254 }
5255
5256 if (status->mirror_image_site_statuses.empty()) {
5257 return -ENOENT;
5258 }
5259
5260 return 0;
5261 }
5262
5263 int image_status_list(cls_method_context_t hctx,
5264 const std::string &start_after, uint64_t max_return,
5265 map<std::string, cls::rbd::MirrorImage> *mirror_images,
5266 map<std::string, cls::rbd::MirrorImageStatus> *mirror_statuses) {
5267 std::string last_read = image_key(start_after);
5268 int max_read = RBD_MAX_KEYS_READ;
5269 bool more = true;
5270
5271 std::set<entity_inst_t> watchers;
5272 int r = list_watchers(hctx, &watchers);
5273 if (r < 0) {
5274 return r;
5275 }
5276
5277 while (more && mirror_images->size() < max_return) {
5278 std::map<std::string, bufferlist> vals;
5279 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
5280 r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX, max_read, &vals,
5281 &more);
5282 if (r < 0) {
5283 if (r != -ENOENT) {
5284 CLS_ERR("error reading mirror image directory by name: %s",
5285 cpp_strerror(r).c_str());
5286 }
5287 return r;
5288 }
5289
5290 for (auto it = vals.begin(); it != vals.end() &&
5291 mirror_images->size() < max_return; ++it) {
5292 const std::string &image_id = it->first.substr(IMAGE_KEY_PREFIX.size());
5293 cls::rbd::MirrorImage mirror_image;
5294 auto iter = it->second.cbegin();
5295 try {
5296 decode(mirror_image, iter);
5297 } catch (const ceph::buffer::error &err) {
5298 CLS_ERR("could not decode mirror image payload of image '%s'",
5299 image_id.c_str());
5300 return -EIO;
5301 }
5302
5303 (*mirror_images)[image_id] = mirror_image;
5304
5305 cls::rbd::MirrorImageStatus status;
5306 int r1 = image_status_get(hctx, mirror_image.global_image_id, watchers,
5307 &status);
5308 if (r1 < 0) {
5309 continue;
5310 }
5311
5312 (*mirror_statuses)[image_id] = status;
5313 }
5314 if (!vals.empty()) {
5315 last_read = image_key(mirror_images->rbegin()->first);
5316 }
5317 }
5318
5319 return 0;
5320 }
5321
5322 cls::rbd::MirrorImageStatusState compute_image_status_summary_state(
5323 cls::rbd::MirrorPeerDirection mirror_peer_direction,
5324 const std::set<std::string>& tx_peer_mirror_uuids,
5325 const cls::rbd::MirrorImageStatus& status) {
5326 std::optional<cls::rbd::MirrorImageStatusState> state = {};
5327
5328 cls::rbd::MirrorImageSiteStatus local_status;
5329 status.get_local_mirror_image_site_status(&local_status);
5330
5331 uint64_t unmatched_tx_peers = 0;
5332 switch (mirror_peer_direction) {
5333 case cls::rbd::MIRROR_PEER_DIRECTION_RX:
5334 // if we are RX-only, summary is based on our local status
5335 if (local_status.up) {
5336 state = local_status.state;
5337 }
5338 break;
5339 case cls::rbd::MIRROR_PEER_DIRECTION_RX_TX:
5340 // if we are RX/TX, combine all statuses
5341 if (local_status.up) {
5342 state = local_status.state;
5343 }
5344 [[fallthrough]];
5345 case cls::rbd::MIRROR_PEER_DIRECTION_TX:
5346 // if we are TX-only, summary is based on remote status
5347 unmatched_tx_peers = tx_peer_mirror_uuids.size();
5348 for (auto& remote_status : status.mirror_image_site_statuses) {
5349 if (remote_status.mirror_uuid ==
5350 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID) {
5351 continue;
5352 }
5353
5354 if (unmatched_tx_peers > 0 &&
5355 tx_peer_mirror_uuids.count(remote_status.mirror_uuid) > 0) {
5356 --unmatched_tx_peers;
5357 }
5358
5359 auto remote_state = (remote_status.up ?
5360 remote_status.state : cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN);
5361 if (remote_status.state == cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR) {
5362 state = remote_status.state;
5363 } else if (!state) {
5364 state = remote_state;
5365 } else if (*state != cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR) {
5366 state = std::min(*state, remote_state);
5367 }
5368 }
5369 break;
5370 default:
5371 break;
5372 }
5373
5374 if (!state || unmatched_tx_peers > 0) {
5375 state = cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN;
5376 }
5377 return *state;
5378 }
5379
5380 int image_status_get_summary(
5381 cls_method_context_t hctx,
5382 cls::rbd::MirrorPeerDirection mirror_peer_direction,
5383 const std::set<std::string>& tx_peer_mirror_uuids,
5384 std::map<cls::rbd::MirrorImageStatusState, int32_t> *states) {
5385 std::set<entity_inst_t> watchers;
5386 int r = list_watchers(hctx, &watchers);
5387 if (r < 0) {
5388 return r;
5389 }
5390
5391 states->clear();
5392
5393 string last_read = IMAGE_KEY_PREFIX;
5394 int max_read = RBD_MAX_KEYS_READ;
5395 bool more = true;
5396 while (more) {
5397 map<string, bufferlist> vals;
5398 r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX,
5399 max_read, &vals, &more);
5400 if (r < 0) {
5401 if (r != -ENOENT) {
5402 CLS_ERR("error reading mirrored images: %s", cpp_strerror(r).c_str());
5403 }
5404 return r;
5405 }
5406
5407 for (auto &list_it : vals) {
5408 const string &key = list_it.first;
5409
5410 if (0 != key.compare(0, IMAGE_KEY_PREFIX.size(), IMAGE_KEY_PREFIX)) {
5411 break;
5412 }
5413
5414 cls::rbd::MirrorImage mirror_image;
5415 auto iter = list_it.second.cbegin();
5416 try {
5417 decode(mirror_image, iter);
5418 } catch (const ceph::buffer::error &err) {
5419 CLS_ERR("could not decode mirror image payload for key '%s'",
5420 key.c_str());
5421 return -EIO;
5422 }
5423
5424 cls::rbd::MirrorImageStatus status;
5425 r = image_status_get(hctx, mirror_image.global_image_id, watchers,
5426 &status);
5427 if (r < 0 && r != -ENOENT) {
5428 return r;
5429 }
5430
5431 auto state = compute_image_status_summary_state(
5432 mirror_peer_direction, tx_peer_mirror_uuids, status);
5433 (*states)[state]++;
5434 }
5435
5436 if (!vals.empty()) {
5437 last_read = vals.rbegin()->first;
5438 }
5439 }
5440
5441 return 0;
5442 }
5443
5444 int image_status_remove_down(cls_method_context_t hctx) {
5445 std::set<entity_inst_t> watchers;
5446 int r = list_watchers(hctx, &watchers);
5447 if (r < 0) {
5448 return r;
5449 }
5450
5451 std::vector<std::string> prefixes = {
5452 STATUS_GLOBAL_KEY_PREFIX, REMOTE_STATUS_GLOBAL_KEY_PREFIX};
5453 for (auto& prefix : prefixes) {
5454 std::string last_read = prefix;
5455 int max_read = RBD_MAX_KEYS_READ;
5456 bool more = true;
5457 while (more) {
5458 std::map<std::string, bufferlist> vals;
5459 r = cls_cxx_map_get_vals(hctx, last_read, prefix, max_read, &vals, &more);
5460 if (r < 0) {
5461 if (r != -ENOENT) {
5462 CLS_ERR("error reading mirrored images: %s", cpp_strerror(r).c_str());
5463 }
5464 return r;
5465 }
5466
5467 for (auto &list_it : vals) {
5468 const std::string &key = list_it.first;
5469
5470 if (0 != key.compare(0, prefix.size(), prefix)) {
5471 break;
5472 }
5473
5474 cls::rbd::MirrorImageSiteStatusOnDisk status;
5475 try {
5476 auto it = list_it.second.cbegin();
5477 status.decode_meta(it);
5478 } catch (const ceph::buffer::error &err) {
5479 CLS_ERR("could not decode status metadata for mirrored image '%s'",
5480 key.c_str());
5481 return -EIO;
5482 }
5483
5484 if (watchers.find(status.origin) == watchers.end()) {
5485 CLS_LOG(20, "removing stale status object for key %s",
5486 key.c_str());
5487 int r1 = cls_cxx_map_remove_key(hctx, key);
5488 if (r1 < 0) {
5489 CLS_ERR("error removing stale status for key '%s': %s",
5490 key.c_str(), cpp_strerror(r1).c_str());
5491 return r1;
5492 }
5493 }
5494 }
5495
5496 if (!vals.empty()) {
5497 last_read = vals.rbegin()->first;
5498 }
5499 }
5500 }
5501
5502 return 0;
5503 }
5504
5505 int image_instance_get(cls_method_context_t hctx,
5506 const string &global_image_id,
5507 const std::set<entity_inst_t> &watchers,
5508 entity_inst_t *instance) {
5509 // instance details only available for local site
5510 bufferlist bl;
5511 int r = cls_cxx_map_get_val(
5512 hctx, status_global_key(global_image_id,
5513 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID),
5514 &bl);
5515 if (r < 0) {
5516 if (r != -ENOENT) {
5517 CLS_ERR("error reading status for mirrored image, global id '%s': '%s'",
5518 global_image_id.c_str(), cpp_strerror(r).c_str());
5519 }
5520 return r;
5521 }
5522
5523 cls::rbd::MirrorImageSiteStatusOnDisk ondisk_status;
5524 try {
5525 auto it = bl.cbegin();
5526 decode(ondisk_status, it);
5527 } catch (const ceph::buffer::error &err) {
5528 CLS_ERR("could not decode status for mirrored image, global id '%s'",
5529 global_image_id.c_str());
5530 return -EIO;
5531 }
5532
5533 if (watchers.find(ondisk_status.origin) == watchers.end()) {
5534 return -ESTALE;
5535 }
5536
5537 *instance = ondisk_status.origin;
5538 return 0;
5539 }
5540
5541 int image_instance_list(cls_method_context_t hctx,
5542 const std::string &start_after,
5543 uint64_t max_return,
5544 map<std::string, entity_inst_t> *instances) {
5545 std::string last_read = image_key(start_after);
5546 int max_read = RBD_MAX_KEYS_READ;
5547 bool more = true;
5548
5549 std::set<entity_inst_t> watchers;
5550 int r = list_watchers(hctx, &watchers);
5551 if (r < 0) {
5552 return r;
5553 }
5554
5555 while (more && instances->size() < max_return) {
5556 std::map<std::string, bufferlist> vals;
5557 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
5558 r = cls_cxx_map_get_vals(hctx, last_read, IMAGE_KEY_PREFIX, max_read, &vals,
5559 &more);
5560 if (r < 0) {
5561 if (r != -ENOENT) {
5562 CLS_ERR("error reading mirror image directory by name: %s",
5563 cpp_strerror(r).c_str());
5564 }
5565 return r;
5566 }
5567
5568 for (auto it = vals.begin(); it != vals.end() &&
5569 instances->size() < max_return; ++it) {
5570 const std::string &image_id = it->first.substr(IMAGE_KEY_PREFIX.size());
5571 cls::rbd::MirrorImage mirror_image;
5572 auto iter = it->second.cbegin();
5573 try {
5574 decode(mirror_image, iter);
5575 } catch (const ceph::buffer::error &err) {
5576 CLS_ERR("could not decode mirror image payload of image '%s'",
5577 image_id.c_str());
5578 return -EIO;
5579 }
5580
5581 entity_inst_t instance;
5582 r = image_instance_get(hctx, mirror_image.global_image_id, watchers,
5583 &instance);
5584 if (r < 0) {
5585 continue;
5586 }
5587
5588 (*instances)[image_id] = instance;
5589 }
5590 if (!vals.empty()) {
5591 last_read = vals.rbegin()->first;
5592 }
5593 }
5594
5595 return 0;
5596 }
5597
5598 int instances_list(cls_method_context_t hctx,
5599 std::vector<std::string> *instance_ids) {
5600 std::string last_read = INSTANCE_KEY_PREFIX;
5601 int max_read = RBD_MAX_KEYS_READ;
5602 bool more = true;
5603 while (more) {
5604 std::map<std::string, bufferlist> vals;
5605 int r = cls_cxx_map_get_vals(hctx, last_read, INSTANCE_KEY_PREFIX.c_str(),
5606 max_read, &vals, &more);
5607 if (r < 0) {
5608 if (r != -ENOENT) {
5609 CLS_ERR("error reading mirror instances: %s", cpp_strerror(r).c_str());
5610 }
5611 return r;
5612 }
5613
5614 for (auto &it : vals) {
5615 instance_ids->push_back(it.first.substr(INSTANCE_KEY_PREFIX.size()));
5616 }
5617
5618 if (!vals.empty()) {
5619 last_read = vals.rbegin()->first;
5620 }
5621 }
5622 return 0;
5623 }
5624
5625 int instances_add(cls_method_context_t hctx, const string &instance_id) {
5626 bufferlist bl;
5627
5628 int r = cls_cxx_map_set_val(hctx, instance_key(instance_id), &bl);
5629 if (r < 0) {
5630 CLS_ERR("error setting mirror instance %s: %s", instance_id.c_str(),
5631 cpp_strerror(r).c_str());
5632 return r;
5633 }
5634 return 0;
5635 }
5636
5637 int instances_remove(cls_method_context_t hctx, const string &instance_id) {
5638
5639 int r = cls_cxx_map_remove_key(hctx, instance_key(instance_id));
5640 if (r < 0) {
5641 CLS_ERR("error removing mirror instance %s: %s", instance_id.c_str(),
5642 cpp_strerror(r).c_str());
5643 return r;
5644 }
5645 return 0;
5646 }
5647
5648 int mirror_image_map_list(cls_method_context_t hctx,
5649 const std::string &start_after,
5650 uint64_t max_return,
5651 std::map<std::string, cls::rbd::MirrorImageMap> *image_mapping) {
5652 bool more = true;
5653 std::string last_read = mirror_image_map_key(start_after);
5654
5655 while (more && image_mapping->size() < max_return) {
5656 std::map<std::string, bufferlist> vals;
5657 CLS_LOG(20, "last read: '%s'", last_read.c_str());
5658
5659 int max_read = std::min<uint64_t>(RBD_MAX_KEYS_READ, max_return - image_mapping->size());
5660 int r = cls_cxx_map_get_vals(hctx, last_read, MIRROR_IMAGE_MAP_KEY_PREFIX,
5661 max_read, &vals, &more);
5662 if (r < 0) {
5663 CLS_ERR("error reading image map: %s", cpp_strerror(r).c_str());
5664 return r;
5665 }
5666
5667 if (vals.empty()) {
5668 return 0;
5669 }
5670
5671 for (auto it = vals.begin(); it != vals.end(); ++it) {
5672 const std::string &global_image_id =
5673 it->first.substr(MIRROR_IMAGE_MAP_KEY_PREFIX.size());
5674
5675 cls::rbd::MirrorImageMap mirror_image_map;
5676 auto iter = it->second.cbegin();
5677 try {
5678 decode(mirror_image_map, iter);
5679 } catch (const ceph::buffer::error &err) {
5680 CLS_ERR("could not decode image map payload: %s",
5681 cpp_strerror(r).c_str());
5682 return -EINVAL;
5683 }
5684
5685 image_mapping->insert(std::make_pair(global_image_id, mirror_image_map));
5686 }
5687
5688 if (!vals.empty()) {
5689 last_read = vals.rbegin()->first;
5690 }
5691 }
5692
5693 return 0;
5694 }
5695
5696 int image_snapshot_unlink_peer(cls_method_context_t hctx,
5697 uint64_t snap_id,
5698 std::string mirror_peer_uuid) {
5699 cls_rbd_snap snap;
5700 std::string snap_key;
5701 key_from_snap_id(snap_id, &snap_key);
5702 int r = read_key(hctx, snap_key, &snap);
5703 if (r < 0) {
5704 if (r != -ENOENT) {
5705 CLS_ERR("Could not read snapshot meta off disk: %s",
5706 cpp_strerror(r).c_str());
5707 }
5708 return r;
5709 }
5710
5711 auto mirror_ns = boost::get<cls::rbd::MirrorSnapshotNamespace>(
5712 &snap.snapshot_namespace);
5713 if (mirror_ns == nullptr) {
5714 CLS_LOG(5, "mirror_image_snapshot_unlink_peer " \
5715 "not mirroring snapshot snap_id=%" PRIu64, snap_id);
5716 return -EINVAL;
5717 }
5718
5719 if (mirror_ns->mirror_peer_uuids.count(mirror_peer_uuid) == 0) {
5720 return -ENOENT;
5721 }
5722
5723 if (mirror_ns->mirror_peer_uuids.size() == 1) {
5724 // if this is the last peer to unlink and we have at least one additional
5725 // newer mirror snapshot, return a special error to inform the caller it
5726 // should remove the snapshot instead.
5727 auto search_lambda = [snap_id](const cls_rbd_snap& snap_meta) {
5728 if (snap_meta.id > snap_id &&
5729 boost::get<cls::rbd::MirrorSnapshotNamespace>(
5730 &snap_meta.snapshot_namespace) != nullptr) {
5731 return -EEXIST;
5732 }
5733 return 0;
5734 };
5735 r = image::snapshot::iterate(hctx, search_lambda);
5736 if (r == -EEXIST) {
5737 return -ERESTART;
5738 }
5739 }
5740
5741 mirror_ns->mirror_peer_uuids.erase(mirror_peer_uuid);
5742
5743 r = image::snapshot::write(hctx, snap_key, std::move(snap));
5744 if (r < 0) {
5745 return r;
5746 }
5747
5748 return 0;
5749 }
5750
5751 int image_snapshot_set_copy_progress(cls_method_context_t hctx,
5752 uint64_t snap_id, bool complete,
5753 uint64_t last_copied_object_number) {
5754 cls_rbd_snap snap;
5755 std::string snap_key;
5756 key_from_snap_id(snap_id, &snap_key);
5757 int r = read_key(hctx, snap_key, &snap);
5758 if (r < 0) {
5759 if (r != -ENOENT) {
5760 CLS_ERR("Could not read snapshot meta off disk: %s",
5761 cpp_strerror(r).c_str());
5762 }
5763 return r;
5764 }
5765
5766 auto mirror_ns = boost::get<cls::rbd::MirrorSnapshotNamespace>(
5767 &snap.snapshot_namespace);
5768 if (mirror_ns == nullptr) {
5769 CLS_LOG(5, "mirror_image_snapshot_set_copy_progress " \
5770 "not mirroring snapshot snap_id=%" PRIu64, snap_id);
5771 return -EINVAL;
5772 }
5773
5774 mirror_ns->complete = complete;
5775 if (mirror_ns->state == cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY ||
5776 mirror_ns->state == cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED) {
5777 mirror_ns->last_copied_object_number = last_copied_object_number;
5778 }
5779
5780 r = image::snapshot::write(hctx, snap_key, std::move(snap));
5781 if (r < 0) {
5782 return r;
5783 }
5784
5785 return 0;
5786 }
5787
5788 } // namespace mirror
5789
5790 /**
5791 * Input:
5792 * none
5793 *
5794 * Output:
5795 * @param uuid (std::string)
5796 * @returns 0 on success, negative error code on failure
5797 */
5798 int mirror_uuid_get(cls_method_context_t hctx, bufferlist *in,
5799 bufferlist *out) {
5800 std::string mirror_uuid;
5801 int r = mirror::uuid_get(hctx, &mirror_uuid);
5802 if (r < 0) {
5803 return r;
5804 }
5805
5806 encode(mirror_uuid, *out);
5807 return 0;
5808 }
5809
5810 /**
5811 * Input:
5812 * @param mirror_uuid (std::string)
5813 *
5814 * Output:
5815 * @returns 0 on success, negative error code on failure
5816 */
5817 int mirror_uuid_set(cls_method_context_t hctx, bufferlist *in,
5818 bufferlist *out) {
5819 std::string mirror_uuid;
5820 try {
5821 auto bl_it = in->cbegin();
5822 decode(mirror_uuid, bl_it);
5823 } catch (const ceph::buffer::error &err) {
5824 return -EINVAL;
5825 }
5826
5827 if (mirror_uuid.empty()) {
5828 CLS_ERR("cannot set empty mirror uuid");
5829 return -EINVAL;
5830 }
5831
5832 uint32_t mirror_mode;
5833 int r = read_key(hctx, mirror::MODE, &mirror_mode);
5834 if (r < 0 && r != -ENOENT) {
5835 return r;
5836 } else if (r == 0 && mirror_mode != cls::rbd::MIRROR_MODE_DISABLED) {
5837 CLS_ERR("cannot set mirror uuid while mirroring enabled");
5838 return -EINVAL;
5839 }
5840
5841 bufferlist mirror_uuid_bl;
5842 mirror_uuid_bl.append(mirror_uuid);
5843 r = cls_cxx_map_set_val(hctx, mirror::UUID, &mirror_uuid_bl);
5844 if (r < 0) {
5845 CLS_ERR("failed to set mirror uuid");
5846 return r;
5847 }
5848 return 0;
5849 }
5850
5851 /**
5852 * Input:
5853 * none
5854 *
5855 * Output:
5856 * @param cls::rbd::MirrorMode (uint32_t)
5857 * @returns 0 on success, negative error code on failure
5858 */
5859 int mirror_mode_get(cls_method_context_t hctx, bufferlist *in,
5860 bufferlist *out) {
5861 uint32_t mirror_mode_decode;
5862 int r = read_key(hctx, mirror::MODE, &mirror_mode_decode);
5863 if (r < 0) {
5864 return r;
5865 }
5866
5867 encode(mirror_mode_decode, *out);
5868 return 0;
5869 }
5870
5871 /**
5872 * Input:
5873 * @param mirror_mode (cls::rbd::MirrorMode) (uint32_t)
5874 *
5875 * Output:
5876 * @returns 0 on success, negative error code on failure
5877 */
5878 int mirror_mode_set(cls_method_context_t hctx, bufferlist *in,
5879 bufferlist *out) {
5880 uint32_t mirror_mode_decode;
5881 try {
5882 auto bl_it = in->cbegin();
5883 decode(mirror_mode_decode, bl_it);
5884 } catch (const ceph::buffer::error &err) {
5885 return -EINVAL;
5886 }
5887
5888 bool enabled;
5889 switch (static_cast<cls::rbd::MirrorMode>(mirror_mode_decode)) {
5890 case cls::rbd::MIRROR_MODE_DISABLED:
5891 enabled = false;
5892 break;
5893 case cls::rbd::MIRROR_MODE_IMAGE:
5894 case cls::rbd::MIRROR_MODE_POOL:
5895 enabled = true;
5896 break;
5897 default:
5898 CLS_ERR("invalid mirror mode: %d", mirror_mode_decode);
5899 return -EINVAL;
5900 }
5901
5902 int r;
5903 if (enabled) {
5904 std::string mirror_uuid;
5905 r = mirror::uuid_get(hctx, &mirror_uuid);
5906 if (r == -ENOENT) {
5907 return -EINVAL;
5908 } else if (r < 0) {
5909 return r;
5910 }
5911
5912 bufferlist bl;
5913 encode(mirror_mode_decode, bl);
5914
5915 r = cls_cxx_map_set_val(hctx, mirror::MODE, &bl);
5916 if (r < 0) {
5917 CLS_ERR("error enabling mirroring: %s", cpp_strerror(r).c_str());
5918 return r;
5919 }
5920 } else {
5921 std::vector<cls::rbd::MirrorPeer> peers;
5922 r = mirror::read_peers(hctx, &peers);
5923 if (r < 0 && r != -ENOENT) {
5924 return r;
5925 }
5926
5927 if (!peers.empty()) {
5928 CLS_ERR("mirroring peers still registered");
5929 return -EBUSY;
5930 }
5931
5932 r = remove_key(hctx, mirror::MODE);
5933 if (r < 0) {
5934 return r;
5935 }
5936
5937 r = remove_key(hctx, mirror::UUID);
5938 if (r < 0) {
5939 return r;
5940 }
5941 }
5942 return 0;
5943 }
5944
5945 /**
5946 * Input:
5947 * @param unique peer site name (std::string)
5948 * @param mirror_uuid (std::string)
5949 * @param direction (MirrorPeerDirection) -- future use
5950 *
5951 * Output:
5952 * @returns 0 on success, negative error code on failure
5953 */
5954 int mirror_peer_ping(cls_method_context_t hctx, bufferlist *in,
5955 bufferlist *out) {
5956 std::string site_name;
5957 std::string mirror_uuid;
5958 cls::rbd::MirrorPeerDirection mirror_peer_direction;
5959 try {
5960 auto it = in->cbegin();
5961 decode(site_name, it);
5962 decode(mirror_uuid, it);
5963
5964 uint8_t direction;
5965 decode(direction, it);
5966 mirror_peer_direction = static_cast<cls::rbd::MirrorPeerDirection>(
5967 direction);
5968 } catch (const ceph::buffer::error &err) {
5969 return -EINVAL;
5970 }
5971
5972 if (mirror_peer_direction != cls::rbd::MIRROR_PEER_DIRECTION_TX) {
5973 return -EINVAL;
5974 }
5975
5976 int r = mirror::peer_ping(hctx, site_name, mirror_uuid);
5977 if (r < 0) {
5978 return r;
5979 }
5980
5981 return 0;
5982 }
5983
5984 /**
5985 * Input:
5986 * none
5987 *
5988 * Output:
5989 * @param std::vector<cls::rbd::MirrorPeer>: collection of peers
5990 * @returns 0 on success, negative error code on failure
5991 */
5992 int mirror_peer_list(cls_method_context_t hctx, bufferlist *in,
5993 bufferlist *out) {
5994 std::vector<cls::rbd::MirrorPeer> peers;
5995 int r = mirror::read_peers(hctx, &peers);
5996 if (r < 0 && r != -ENOENT) {
5997 return r;
5998 }
5999
6000 encode(peers, *out);
6001 return 0;
6002 }
6003
6004 /**
6005 * Input:
6006 * @param mirror_peer (cls::rbd::MirrorPeer)
6007 *
6008 * Output:
6009 * @returns 0 on success, negative error code on failure
6010 */
6011 int mirror_peer_add(cls_method_context_t hctx, bufferlist *in,
6012 bufferlist *out) {
6013 cls::rbd::MirrorPeer mirror_peer;
6014 try {
6015 auto it = in->cbegin();
6016 decode(mirror_peer, it);
6017 } catch (const ceph::buffer::error &err) {
6018 return -EINVAL;
6019 }
6020
6021 int r = mirror::peer_add(hctx, mirror_peer);
6022 if (r < 0) {
6023 return r;
6024 }
6025
6026 return 0;
6027 }
6028
6029 /**
6030 * Input:
6031 * @param uuid (std::string)
6032 *
6033 * Output:
6034 * @returns 0 on success, negative error code on failure
6035 */
6036 int mirror_peer_remove(cls_method_context_t hctx, bufferlist *in,
6037 bufferlist *out) {
6038 std::string uuid;
6039 try {
6040 auto it = in->cbegin();
6041 decode(uuid, it);
6042 } catch (const ceph::buffer::error &err) {
6043 return -EINVAL;
6044 }
6045
6046 int r = mirror::peer_remove(hctx, uuid);
6047 if (r < 0) {
6048 return r;
6049 }
6050 return 0;
6051 }
6052
6053 /**
6054 * Input:
6055 * @param uuid (std::string)
6056 * @param client_name (std::string)
6057 *
6058 * Output:
6059 * @returns 0 on success, negative error code on failure
6060 */
6061 int mirror_peer_set_client(cls_method_context_t hctx, bufferlist *in,
6062 bufferlist *out) {
6063 std::string uuid;
6064 std::string client_name;
6065 try {
6066 auto it = in->cbegin();
6067 decode(uuid, it);
6068 decode(client_name, it);
6069 } catch (const ceph::buffer::error &err) {
6070 return -EINVAL;
6071 }
6072
6073 cls::rbd::MirrorPeer peer;
6074 int r = mirror::read_peer(hctx, uuid, &peer);
6075 if (r < 0) {
6076 return r;
6077 }
6078
6079 peer.client_name = client_name;
6080 r = mirror::write_peer(hctx, peer);
6081 if (r < 0) {
6082 return r;
6083 }
6084 return 0;
6085 }
6086
6087 /**
6088 * Input:
6089 * @param uuid (std::string)
6090 * @param site_name (std::string)
6091 *
6092 * Output:
6093 * @returns 0 on success, negative error code on failure
6094 */
6095 int mirror_peer_set_cluster(cls_method_context_t hctx, bufferlist *in,
6096 bufferlist *out) {
6097 std::string uuid;
6098 std::string site_name;
6099 try {
6100 auto it = in->cbegin();
6101 decode(uuid, it);
6102 decode(site_name, it);
6103 } catch (const ceph::buffer::error &err) {
6104 return -EINVAL;
6105 }
6106
6107 cls::rbd::MirrorPeer* peer = nullptr;
6108 std::vector<cls::rbd::MirrorPeer> peers;
6109 int r = mirror::read_peers(hctx, &peers);
6110 if (r < 0 && r != -ENOENT) {
6111 return r;
6112 }
6113
6114 for (auto& p : peers) {
6115 if (p.uuid == uuid) {
6116 peer = &p;
6117 } else if (p.site_name == site_name) {
6118 return -EEXIST;
6119 }
6120 }
6121
6122 if (peer == nullptr) {
6123 return -ENOENT;
6124 }
6125
6126 peer->site_name = site_name;
6127 r = mirror::write_peer(hctx, *peer);
6128 if (r < 0) {
6129 return r;
6130 }
6131 return 0;
6132 }
6133
6134 /**
6135 * Input:
6136 * @param uuid (std::string)
6137 * @param direction (uint8_t)
6138 *
6139 * Output:
6140 * @returns 0 on success, negative error code on failure
6141 */
6142 int mirror_peer_set_direction(cls_method_context_t hctx, bufferlist *in,
6143 bufferlist *out) {
6144 std::string uuid;
6145 cls::rbd::MirrorPeerDirection mirror_peer_direction;
6146 try {
6147 auto it = in->cbegin();
6148 decode(uuid, it);
6149 uint8_t direction;
6150 decode(direction, it);
6151 mirror_peer_direction = static_cast<cls::rbd::MirrorPeerDirection>(
6152 direction);
6153 } catch (const ceph::buffer::error &err) {
6154 return -EINVAL;
6155 }
6156
6157 cls::rbd::MirrorPeer peer;
6158 int r = mirror::read_peer(hctx, uuid, &peer);
6159 if (r < 0) {
6160 return r;
6161 }
6162
6163 peer.mirror_peer_direction = mirror_peer_direction;
6164 r = mirror::write_peer(hctx, peer);
6165 if (r < 0) {
6166 return r;
6167 }
6168 return 0;
6169 }
6170
6171 /**
6172 * Input:
6173 * @param start_after which name to begin listing after
6174 * (use the empty string to start at the beginning)
6175 * @param max_return the maximum number of names to list
6176 *
6177 * Output:
6178 * @param std::map<std::string, std::string>: local id to global id map
6179 * @returns 0 on success, negative error code on failure
6180 */
6181 int mirror_image_list(cls_method_context_t hctx, bufferlist *in,
6182 bufferlist *out) {
6183 std::string start_after;
6184 uint64_t max_return;
6185 try {
6186 auto iter = in->cbegin();
6187 decode(start_after, iter);
6188 decode(max_return, iter);
6189 } catch (const ceph::buffer::error &err) {
6190 return -EINVAL;
6191 }
6192
6193 int max_read = RBD_MAX_KEYS_READ;
6194 bool more = true;
6195 std::map<std::string, std::string> mirror_images;
6196 std::string last_read = mirror::image_key(start_after);
6197
6198 while (more && mirror_images.size() < max_return) {
6199 std::map<std::string, bufferlist> vals;
6200 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
6201 int r = cls_cxx_map_get_vals(hctx, last_read, mirror::IMAGE_KEY_PREFIX,
6202 max_read, &vals, &more);
6203 if (r < 0) {
6204 if (r != -ENOENT) {
6205 CLS_ERR("error reading mirror image directory by name: %s",
6206 cpp_strerror(r).c_str());
6207 }
6208 return r;
6209 }
6210
6211 for (auto it = vals.begin(); it != vals.end(); ++it) {
6212 const std::string &image_id =
6213 it->first.substr(mirror::IMAGE_KEY_PREFIX.size());
6214 cls::rbd::MirrorImage mirror_image;
6215 auto iter = it->second.cbegin();
6216 try {
6217 decode(mirror_image, iter);
6218 } catch (const ceph::buffer::error &err) {
6219 CLS_ERR("could not decode mirror image payload of image '%s'",
6220 image_id.c_str());
6221 return -EIO;
6222 }
6223
6224 mirror_images[image_id] = mirror_image.global_image_id;
6225 if (mirror_images.size() >= max_return) {
6226 break;
6227 }
6228 }
6229 if (!vals.empty()) {
6230 last_read = mirror::image_key(mirror_images.rbegin()->first);
6231 }
6232 }
6233
6234 encode(mirror_images, *out);
6235 return 0;
6236 }
6237
6238 /**
6239 * Input:
6240 * @param global_id (std::string)
6241 *
6242 * Output:
6243 * @param std::string - image id
6244 * @returns 0 on success, negative error code on failure
6245 */
6246 int mirror_image_get_image_id(cls_method_context_t hctx, bufferlist *in,
6247 bufferlist *out) {
6248 std::string global_id;
6249 try {
6250 auto it = in->cbegin();
6251 decode(global_id, it);
6252 } catch (const ceph::buffer::error &err) {
6253 return -EINVAL;
6254 }
6255
6256 std::string image_id;
6257 int r = read_key(hctx, mirror::global_key(global_id), &image_id);
6258 if (r < 0) {
6259 if (r != -ENOENT) {
6260 CLS_ERR("error retrieving image id for global id '%s': %s",
6261 global_id.c_str(), cpp_strerror(r).c_str());
6262 }
6263 return r;
6264 }
6265
6266 encode(image_id, *out);
6267 return 0;
6268 }
6269
6270 /**
6271 * Input:
6272 * @param image_id (std::string)
6273 *
6274 * Output:
6275 * @param cls::rbd::MirrorImage - metadata associated with the image_id
6276 * @returns 0 on success, negative error code on failure
6277 */
6278 int mirror_image_get(cls_method_context_t hctx, bufferlist *in,
6279 bufferlist *out) {
6280 string image_id;
6281 try {
6282 auto it = in->cbegin();
6283 decode(image_id, it);
6284 } catch (const ceph::buffer::error &err) {
6285 return -EINVAL;
6286 }
6287
6288 cls::rbd::MirrorImage mirror_image;
6289 int r = mirror::image_get(hctx, image_id, &mirror_image);
6290 if (r < 0) {
6291 return r;
6292 }
6293
6294 encode(mirror_image, *out);
6295 return 0;
6296 }
6297
6298 /**
6299 * Input:
6300 * @param image_id (std::string)
6301 * @param mirror_image (cls::rbd::MirrorImage)
6302 *
6303 * Output:
6304 * @returns 0 on success, negative error code on failure
6305 * @returns -EEXIST if there's an existing image_id with a different global_image_id
6306 */
6307 int mirror_image_set(cls_method_context_t hctx, bufferlist *in,
6308 bufferlist *out) {
6309 string image_id;
6310 cls::rbd::MirrorImage mirror_image;
6311 try {
6312 auto it = in->cbegin();
6313 decode(image_id, it);
6314 decode(mirror_image, it);
6315 } catch (const ceph::buffer::error &err) {
6316 return -EINVAL;
6317 }
6318
6319 int r = mirror::image_set(hctx, image_id, mirror_image);
6320 if (r < 0) {
6321 return r;
6322 }
6323 return 0;
6324 }
6325
6326 /**
6327 * Input:
6328 * @param image_id (std::string)
6329 *
6330 * Output:
6331 * @returns 0 on success, negative error code on failure
6332 */
6333 int mirror_image_remove(cls_method_context_t hctx, bufferlist *in,
6334 bufferlist *out) {
6335 string image_id;
6336 try {
6337 auto it = in->cbegin();
6338 decode(image_id, it);
6339 } catch (const ceph::buffer::error &err) {
6340 return -EINVAL;
6341 }
6342
6343 int r = mirror::image_remove(hctx, image_id);
6344 if (r < 0) {
6345 return r;
6346 }
6347 return 0;
6348 }
6349
6350 /**
6351 * Input:
6352 * @param global_image_id (std::string)
6353 * @param status (cls::rbd::MirrorImageSiteStatus)
6354 *
6355 * Output:
6356 * @returns 0 on success, negative error code on failure
6357 */
6358 int mirror_image_status_set(cls_method_context_t hctx, bufferlist *in,
6359 bufferlist *out) {
6360 string global_image_id;
6361 cls::rbd::MirrorImageSiteStatus status;
6362 try {
6363 auto it = in->cbegin();
6364 decode(global_image_id, it);
6365 decode(status, it);
6366 } catch (const ceph::buffer::error &err) {
6367 return -EINVAL;
6368 }
6369
6370 int r = mirror::image_status_set(hctx, global_image_id, status);
6371 if (r < 0) {
6372 return r;
6373 }
6374 return 0;
6375 }
6376
6377 /**
6378 * Input:
6379 * @param global_image_id (std::string)
6380 *
6381 * Output:
6382 * @returns 0 on success, negative error code on failure
6383 *
6384 */
6385 int mirror_image_status_remove(cls_method_context_t hctx, bufferlist *in,
6386 bufferlist *out) {
6387 string global_image_id;
6388 try {
6389 auto it = in->cbegin();
6390 decode(global_image_id, it);
6391 } catch (const ceph::buffer::error &err) {
6392 return -EINVAL;
6393 }
6394
6395 int r = mirror::image_status_remove(hctx, global_image_id);
6396 if (r < 0) {
6397 return r;
6398 }
6399 return 0;
6400 }
6401
6402 /**
6403 * Input:
6404 * @param global_image_id (std::string)
6405 *
6406 * Output:
6407 * @param cls::rbd::MirrorImageStatus - metadata associated with the global_image_id
6408 * @returns 0 on success, negative error code on failure
6409 */
6410 int mirror_image_status_get(cls_method_context_t hctx, bufferlist *in,
6411 bufferlist *out) {
6412 string global_image_id;
6413 try {
6414 auto it = in->cbegin();
6415 decode(global_image_id, it);
6416 } catch (const ceph::buffer::error &err) {
6417 return -EINVAL;
6418 }
6419
6420 std::set<entity_inst_t> watchers;
6421 int r = mirror::list_watchers(hctx, &watchers);
6422 if (r < 0) {
6423 return r;
6424 }
6425
6426 cls::rbd::MirrorImageStatus status;
6427 r = mirror::image_status_get(hctx, global_image_id, watchers, &status);
6428 if (r < 0) {
6429 return r;
6430 }
6431
6432 encode(status, *out);
6433 return 0;
6434 }
6435
6436 /**
6437 * Input:
6438 * @param start_after which name to begin listing after
6439 * (use the empty string to start at the beginning)
6440 * @param max_return the maximum number of names to list
6441 *
6442 * Output:
6443 * @param std::map<std::string, cls::rbd::MirrorImage>: image id to image map
6444 * @param std::map<std::string, cls::rbd::MirrorImageStatus>: image it to status map
6445 * @returns 0 on success, negative error code on failure
6446 */
6447 int mirror_image_status_list(cls_method_context_t hctx, bufferlist *in,
6448 bufferlist *out) {
6449 std::string start_after;
6450 uint64_t max_return;
6451 try {
6452 auto iter = in->cbegin();
6453 decode(start_after, iter);
6454 decode(max_return, iter);
6455 } catch (const ceph::buffer::error &err) {
6456 return -EINVAL;
6457 }
6458
6459 map<std::string, cls::rbd::MirrorImage> images;
6460 map<std::string, cls::rbd::MirrorImageStatus> statuses;
6461 int r = mirror::image_status_list(hctx, start_after, max_return, &images,
6462 &statuses);
6463 if (r < 0) {
6464 return r;
6465 }
6466
6467 encode(images, *out);
6468 encode(statuses, *out);
6469 return 0;
6470 }
6471
6472 /**
6473 * Input:
6474 * @param std::vector<cls::rbd::MirrorPeer> - optional peers (backwards compatibility)
6475 *
6476 * Output:
6477 * @param std::map<cls::rbd::MirrorImageStatusState, int32_t>: states counts
6478 * @returns 0 on success, negative error code on failure
6479 */
6480 int mirror_image_status_get_summary(cls_method_context_t hctx, bufferlist *in,
6481 bufferlist *out) {
6482 std::vector<cls::rbd::MirrorPeer> peers;
6483 try {
6484 auto iter = in->cbegin();
6485 if (!iter.end()) {
6486 decode(peers, iter);
6487 }
6488 } catch (const ceph::buffer::error &err) {
6489 return -EINVAL;
6490 }
6491
6492 auto mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_RX;
6493 if (!peers.empty()) {
6494 mirror_peer_direction = peers.begin()->mirror_peer_direction;
6495 }
6496
6497 std::set<std::string> tx_peer_mirror_uuids;
6498 for (auto& peer : peers) {
6499 if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) {
6500 continue;
6501 }
6502
6503 tx_peer_mirror_uuids.insert(peer.mirror_uuid);
6504 if (mirror_peer_direction != cls::rbd::MIRROR_PEER_DIRECTION_RX_TX &&
6505 mirror_peer_direction != peer.mirror_peer_direction) {
6506 mirror_peer_direction = cls::rbd::MIRROR_PEER_DIRECTION_RX_TX;
6507 }
6508 }
6509
6510 std::map<cls::rbd::MirrorImageStatusState, int32_t> states;
6511 int r = mirror::image_status_get_summary(hctx, mirror_peer_direction,
6512 tx_peer_mirror_uuids, &states);
6513 if (r < 0) {
6514 return r;
6515 }
6516
6517 encode(states, *out);
6518 return 0;
6519 }
6520
6521 /**
6522 * Input:
6523 * none
6524 *
6525 * Output:
6526 * @returns 0 on success, negative error code on failure
6527 */
6528 int mirror_image_status_remove_down(cls_method_context_t hctx, bufferlist *in,
6529 bufferlist *out) {
6530 int r = mirror::image_status_remove_down(hctx);
6531 if (r < 0) {
6532 return r;
6533 }
6534 return 0;
6535 }
6536
6537 /**
6538 * Input:
6539 * @param global_image_id (std::string)
6540 *
6541 * Output:
6542 * @param entity_inst_t - instance
6543 * @returns 0 on success, negative error code on failure
6544 */
6545 int mirror_image_instance_get(cls_method_context_t hctx, bufferlist *in,
6546 bufferlist *out) {
6547 string global_image_id;
6548 try {
6549 auto it = in->cbegin();
6550 decode(global_image_id, it);
6551 } catch (const ceph::buffer::error &err) {
6552 return -EINVAL;
6553 }
6554
6555 std::set<entity_inst_t> watchers;
6556 int r = mirror::list_watchers(hctx, &watchers);
6557 if (r < 0) {
6558 return r;
6559 }
6560
6561 entity_inst_t instance;
6562 r = mirror::image_instance_get(hctx, global_image_id, watchers, &instance);
6563 if (r < 0) {
6564 return r;
6565 }
6566
6567 encode(instance, *out, cls_get_features(hctx));
6568 return 0;
6569 }
6570
6571 /**
6572 * Input:
6573 * @param start_after which name to begin listing after
6574 * (use the empty string to start at the beginning)
6575 * @param max_return the maximum number of names to list
6576 *
6577 * Output:
6578 * @param std::map<std::string, entity_inst_t>: image id to instance map
6579 * @returns 0 on success, negative error code on failure
6580 */
6581 int mirror_image_instance_list(cls_method_context_t hctx, bufferlist *in,
6582 bufferlist *out) {
6583 std::string start_after;
6584 uint64_t max_return;
6585 try {
6586 auto iter = in->cbegin();
6587 decode(start_after, iter);
6588 decode(max_return, iter);
6589 } catch (const ceph::buffer::error &err) {
6590 return -EINVAL;
6591 }
6592
6593 map<std::string, entity_inst_t> instances;
6594 int r = mirror::image_instance_list(hctx, start_after, max_return,
6595 &instances);
6596 if (r < 0) {
6597 return r;
6598 }
6599
6600 encode(instances, *out, cls_get_features(hctx));
6601 return 0;
6602 }
6603
6604 /**
6605 * Input:
6606 * none
6607 *
6608 * Output:
6609 * @param std::vector<std::string>: instance ids
6610 * @returns 0 on success, negative error code on failure
6611 */
6612 int mirror_instances_list(cls_method_context_t hctx, bufferlist *in,
6613 bufferlist *out) {
6614 std::vector<std::string> instance_ids;
6615
6616 int r = mirror::instances_list(hctx, &instance_ids);
6617 if (r < 0) {
6618 return r;
6619 }
6620
6621 encode(instance_ids, *out);
6622 return 0;
6623 }
6624
6625 /**
6626 * Input:
6627 * @param instance_id (std::string)
6628 *
6629 * Output:
6630 * @returns 0 on success, negative error code on failure
6631 */
6632 int mirror_instances_add(cls_method_context_t hctx, bufferlist *in,
6633 bufferlist *out) {
6634 std::string instance_id;
6635 try {
6636 auto iter = in->cbegin();
6637 decode(instance_id, iter);
6638 } catch (const ceph::buffer::error &err) {
6639 return -EINVAL;
6640 }
6641
6642 int r = mirror::instances_add(hctx, instance_id);
6643 if (r < 0) {
6644 return r;
6645 }
6646 return 0;
6647 }
6648
6649 /**
6650 * Input:
6651 * @param instance_id (std::string)
6652 *
6653 * Output:
6654 * @returns 0 on success, negative error code on failure
6655 */
6656 int mirror_instances_remove(cls_method_context_t hctx, bufferlist *in,
6657 bufferlist *out) {
6658 std::string instance_id;
6659 try {
6660 auto iter = in->cbegin();
6661 decode(instance_id, iter);
6662 } catch (const ceph::buffer::error &err) {
6663 return -EINVAL;
6664 }
6665
6666 int r = mirror::instances_remove(hctx, instance_id);
6667 if (r < 0) {
6668 return r;
6669 }
6670 return 0;
6671 }
6672
6673 /**
6674 * Input:
6675 * @param start_after: key to start after
6676 * @param max_return: max return items
6677 *
6678 * Output:
6679 * @param std::map<std::string, cls::rbd::MirrorImageMap>: image mapping
6680 * @returns 0 on success, negative error code on failure
6681 */
6682 int mirror_image_map_list(cls_method_context_t hctx, bufferlist *in,
6683 bufferlist *out) {
6684 std::string start_after;
6685 uint64_t max_return;
6686 try {
6687 auto it = in->cbegin();
6688 decode(start_after, it);
6689 decode(max_return, it);
6690 } catch (const ceph::buffer::error &err) {
6691 return -EINVAL;
6692 }
6693
6694 std::map<std::string, cls::rbd::MirrorImageMap> image_mapping;
6695 int r = mirror::mirror_image_map_list(hctx, start_after, max_return, &image_mapping);
6696 if (r < 0) {
6697 return r;
6698 }
6699
6700 encode(image_mapping, *out);
6701 return 0;
6702 }
6703
6704 /**
6705 * Input:
6706 * @param global_image_id: global image id
6707 * @param image_map: image map
6708 *
6709 * Output:
6710 * @returns 0 on success, negative error code on failure
6711 */
6712 int mirror_image_map_update(cls_method_context_t hctx, bufferlist *in,
6713 bufferlist *out) {
6714 std::string global_image_id;
6715 cls::rbd::MirrorImageMap image_map;
6716
6717 try {
6718 auto it = in->cbegin();
6719 decode(global_image_id, it);
6720 decode(image_map, it);
6721 } catch (const ceph::buffer::error &err) {
6722 return -EINVAL;
6723 }
6724
6725 bufferlist bl;
6726 encode(image_map, bl);
6727
6728 const std::string key = mirror::mirror_image_map_key(global_image_id);
6729 int r = cls_cxx_map_set_val(hctx, key, &bl);
6730 if (r < 0) {
6731 CLS_ERR("error updating image map %s: %s", key.c_str(),
6732 cpp_strerror(r).c_str());
6733 return r;
6734 }
6735
6736 return 0;
6737 }
6738
6739 /**
6740 * Input:
6741 * @param global_image_id: global image id
6742 *
6743 * Output:
6744 * @returns 0 on success, negative error code on failure
6745 */
6746 int mirror_image_map_remove(cls_method_context_t hctx, bufferlist *in,
6747 bufferlist *out) {
6748 std::string global_image_id;
6749
6750 try {
6751 auto it = in->cbegin();
6752 decode(global_image_id, it);
6753 } catch (const ceph::buffer::error &err) {
6754 return -EINVAL;
6755 }
6756
6757 const std::string key = mirror::mirror_image_map_key(global_image_id);
6758 int r = cls_cxx_map_remove_key(hctx, key);
6759 if (r < 0 && r != -ENOENT) {
6760 CLS_ERR("error removing image map %s: %s", key.c_str(),
6761 cpp_strerror(r).c_str());
6762 return r;
6763 }
6764
6765 return 0;
6766 }
6767
6768
6769 /**
6770 * Input:
6771 * @param snap_id: snapshot id
6772 * @param mirror_peer_uuid: mirror peer uuid
6773 *
6774 * Output:
6775 * @returns 0 on success, negative error code on failure
6776 */
6777 int mirror_image_snapshot_unlink_peer(cls_method_context_t hctx, bufferlist *in,
6778 bufferlist *out) {
6779 uint64_t snap_id;
6780 std::string mirror_peer_uuid;
6781 try {
6782 auto iter = in->cbegin();
6783 decode(snap_id, iter);
6784 decode(mirror_peer_uuid, iter);
6785 } catch (const ceph::buffer::error &err) {
6786 return -EINVAL;
6787 }
6788
6789 CLS_LOG(20,
6790 "mirror_image_snapshot_unlink_peer snap_id=%" PRIu64 " peer_uuid=%s",
6791 snap_id, mirror_peer_uuid.c_str());
6792
6793 int r = mirror::image_snapshot_unlink_peer(hctx, snap_id, mirror_peer_uuid);
6794 if (r < 0) {
6795 return r;
6796 }
6797 return 0;
6798 }
6799
6800 /**
6801 * Input:
6802 * @param snap_id: snapshot id
6803 * @param complete: true if shapshot fully copied/complete
6804 * @param last_copied_object_number: last copied object number
6805 *
6806 * Output:
6807 * @returns 0 on success, negative error code on failure
6808 */
6809 int mirror_image_snapshot_set_copy_progress(cls_method_context_t hctx,
6810 bufferlist *in,
6811 bufferlist *out) {
6812 uint64_t snap_id;
6813 bool complete;
6814 uint64_t last_copied_object_number;
6815 try {
6816 auto iter = in->cbegin();
6817 decode(snap_id, iter);
6818 decode(complete, iter);
6819 decode(last_copied_object_number, iter);
6820 } catch (const ceph::buffer::error &err) {
6821 return -EINVAL;
6822 }
6823
6824 CLS_LOG(20, "mirror_image_snapshot_set_copy_progress snap_id=%" PRIu64 \
6825 " complete=%d last_copied_object_number=%" PRIu64, snap_id, complete,
6826 last_copied_object_number);
6827
6828 int r = mirror::image_snapshot_set_copy_progress(hctx, snap_id, complete,
6829 last_copied_object_number);
6830 if (r < 0) {
6831 return r;
6832 }
6833 return 0;
6834 }
6835
6836 namespace group {
6837
6838 /********************** methods for rbd_group_directory ***********************/
6839
6840 int dir_add(cls_method_context_t hctx,
6841 const string &name, const string &id,
6842 bool check_for_unique_id)
6843 {
6844 if (!name.size() || !is_valid_id(id)) {
6845 CLS_ERR("invalid group name '%s' or id '%s'",
6846 name.c_str(), id.c_str());
6847 return -EINVAL;
6848 }
6849
6850 CLS_LOG(20, "dir_add name=%s id=%s", name.c_str(), id.c_str());
6851
6852 string name_key = dir_key_for_name(name);
6853 string id_key = dir_key_for_id(id);
6854 string tmp;
6855 int r = read_key(hctx, name_key, &tmp);
6856 if (r != -ENOENT) {
6857 CLS_LOG(10, "name already exists");
6858 return -EEXIST;
6859 }
6860 r = read_key(hctx, id_key, &tmp);
6861 if (r != -ENOENT && check_for_unique_id) {
6862 CLS_LOG(10, "id already exists");
6863 return -EBADF;
6864 }
6865 bufferlist id_bl, name_bl;
6866 encode(id, id_bl);
6867 encode(name, name_bl);
6868 map<string, bufferlist> omap_vals;
6869 omap_vals[name_key] = id_bl;
6870 omap_vals[id_key] = name_bl;
6871 return cls_cxx_map_set_vals(hctx, &omap_vals);
6872 }
6873
6874 int dir_remove(cls_method_context_t hctx,
6875 const string &name, const string &id)
6876 {
6877 CLS_LOG(20, "dir_remove name=%s id=%s", name.c_str(), id.c_str());
6878
6879 string name_key = dir_key_for_name(name);
6880 string id_key = dir_key_for_id(id);
6881 string stored_name, stored_id;
6882
6883 int r = read_key(hctx, name_key, &stored_id);
6884 if (r < 0) {
6885 if (r != -ENOENT)
6886 CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r).c_str());
6887 return r;
6888 }
6889 r = read_key(hctx, id_key, &stored_name);
6890 if (r < 0) {
6891 if (r != -ENOENT)
6892 CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r).c_str());
6893 return r;
6894 }
6895
6896 // check if this op raced with a rename
6897 if (stored_name != name || stored_id != id) {
6898 CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
6899 stored_name.c_str(), stored_id.c_str(), name.c_str(), id.c_str());
6900 return -ESTALE;
6901 }
6902
6903 r = cls_cxx_map_remove_key(hctx, name_key);
6904 if (r < 0) {
6905 CLS_ERR("error removing name: %s", cpp_strerror(r).c_str());
6906 return r;
6907 }
6908
6909 r = cls_cxx_map_remove_key(hctx, id_key);
6910 if (r < 0) {
6911 CLS_ERR("error removing id: %s", cpp_strerror(r).c_str());
6912 return r;
6913 }
6914
6915 return 0;
6916 }
6917
6918 static const string RBD_GROUP_SNAP_KEY_PREFIX = "snapshot_";
6919
6920 std::string snap_key(const std::string &snap_id) {
6921 ostringstream oss;
6922 oss << RBD_GROUP_SNAP_KEY_PREFIX << snap_id;
6923 return oss.str();
6924 }
6925
6926 int snap_list(cls_method_context_t hctx, cls::rbd::GroupSnapshot start_after,
6927 uint64_t max_return,
6928 std::vector<cls::rbd::GroupSnapshot> *group_snaps)
6929 {
6930 int max_read = RBD_MAX_KEYS_READ;
6931 std::map<string, bufferlist> vals;
6932 string last_read = snap_key(start_after.id);
6933
6934 group_snaps->clear();
6935
6936 bool more;
6937 do {
6938 int r = cls_cxx_map_get_vals(hctx, last_read,
6939 RBD_GROUP_SNAP_KEY_PREFIX,
6940 max_read, &vals, &more);
6941 if (r < 0)
6942 return r;
6943
6944 for (auto it = vals.begin(); it != vals.end() && group_snaps->size() < max_return; ++it) {
6945
6946 auto iter = it->second.cbegin();
6947 cls::rbd::GroupSnapshot snap;
6948 try {
6949 decode(snap, iter);
6950 } catch (const ceph::buffer::error &err) {
6951 CLS_ERR("error decoding snapshot: %s", it->first.c_str());
6952 return -EIO;
6953 }
6954 CLS_LOG(20, "Discovered snapshot %s %s",
6955 snap.name.c_str(),
6956 snap.id.c_str());
6957 group_snaps->push_back(snap);
6958 }
6959
6960 } while (more && (group_snaps->size() < max_return));
6961
6962 return 0;
6963 }
6964
6965 static int check_duplicate_snap_name(cls_method_context_t hctx,
6966 const std::string &snap_name,
6967 const std::string &snap_id)
6968 {
6969 const int max_read = 1024;
6970 cls::rbd::GroupSnapshot snap_last;
6971 std::vector<cls::rbd::GroupSnapshot> page;
6972
6973 for (;;) {
6974 int r = snap_list(hctx, snap_last, max_read, &page);
6975 if (r < 0) {
6976 return r;
6977 }
6978 for (auto& snap: page) {
6979 if (snap.name == snap_name && snap.id != snap_id) {
6980 return -EEXIST;
6981 }
6982 }
6983
6984 if (page.size() < max_read) {
6985 break;
6986 }
6987
6988 snap_last = *page.rbegin();
6989 }
6990
6991 return 0;
6992 }
6993
6994 } // namespace group
6995
6996 /**
6997 * List groups from the directory.
6998 *
6999 * Input:
7000 * @param start_after (std::string)
7001 * @param max_return (int64_t)
7002 *
7003 * Output:
7004 * @param map of groups (name, id)
7005 * @return 0 on success, negative error code on failure
7006 */
7007 int group_dir_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7008 {
7009 string start_after;
7010 uint64_t max_return;
7011
7012 try {
7013 auto iter = in->cbegin();
7014 decode(start_after, iter);
7015 decode(max_return, iter);
7016 } catch (const ceph::buffer::error &err) {
7017 return -EINVAL;
7018 }
7019
7020 int max_read = RBD_MAX_KEYS_READ;
7021 bool more = true;
7022 map<string, string> groups;
7023 string last_read = dir_key_for_name(start_after);
7024
7025 while (more && groups.size() < max_return) {
7026 map<string, bufferlist> vals;
7027 CLS_LOG(20, "last_read = '%s'", last_read.c_str());
7028 int r = cls_cxx_map_get_vals(hctx, last_read, RBD_DIR_NAME_KEY_PREFIX,
7029 max_read, &vals, &more);
7030 if (r < 0) {
7031 if (r != -ENOENT) {
7032 CLS_ERR("error reading directory by name: %s", cpp_strerror(r).c_str());
7033 }
7034 return r;
7035 }
7036
7037 for (auto val : vals) {
7038 string id;
7039 auto iter = val.second.cbegin();
7040 try {
7041 decode(id, iter);
7042 } catch (const ceph::buffer::error &err) {
7043 CLS_ERR("could not decode id of group '%s'", val.first.c_str());
7044 return -EIO;
7045 }
7046 CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(val.first).c_str(), id.c_str());
7047 groups[dir_name_from_key(val.first)] = id;
7048 if (groups.size() >= max_return)
7049 break;
7050 }
7051 if (!vals.empty()) {
7052 last_read = dir_key_for_name(groups.rbegin()->first);
7053 }
7054 }
7055
7056 encode(groups, *out);
7057
7058 return 0;
7059 }
7060
7061 /**
7062 * Add a group to the directory.
7063 *
7064 * Input:
7065 * @param name (std::string)
7066 * @param id (std::string)
7067 *
7068 * Output:
7069 * @return 0 on success, negative error code on failure
7070 */
7071 int group_dir_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7072 {
7073 int r = cls_cxx_create(hctx, false);
7074
7075 if (r < 0) {
7076 CLS_ERR("could not create group directory: %s",
7077 cpp_strerror(r).c_str());
7078 return r;
7079 }
7080
7081 string name, id;
7082 try {
7083 auto iter = in->cbegin();
7084 decode(name, iter);
7085 decode(id, iter);
7086 } catch (const ceph::buffer::error &err) {
7087 return -EINVAL;
7088 }
7089
7090 return group::dir_add(hctx, name, id, true);
7091 }
7092
7093 /**
7094 * Rename a group to the directory.
7095 *
7096 * Input:
7097 * @param src original name of the group (std::string)
7098 * @param dest new name of the group (std::string)
7099 * @param id the id of the group (std::string)
7100 *
7101 * Output:
7102 * @return 0 on success, negative error code on failure
7103 */
7104 int group_dir_rename(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7105 {
7106 string src, dest, id;
7107 try {
7108 auto iter = in->cbegin();
7109 decode(src, iter);
7110 decode(dest, iter);
7111 decode(id, iter);
7112 } catch (const ceph::buffer::error &err) {
7113 return -EINVAL;
7114 }
7115
7116 int r = group::dir_remove(hctx, src, id);
7117 if (r < 0)
7118 return r;
7119
7120 return group::dir_add(hctx, dest, id, false);
7121 }
7122
7123 /**
7124 * Remove a group from the directory.
7125 *
7126 * Input:
7127 * @param name (std::string)
7128 * @param id (std::string)
7129 *
7130 * Output:
7131 * @return 0 on success, negative error code on failure
7132 */
7133 int group_dir_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7134 {
7135 string name, id;
7136 try {
7137 auto iter = in->cbegin();
7138 decode(name, iter);
7139 decode(id, iter);
7140 } catch (const ceph::buffer::error &err) {
7141 return -EINVAL;
7142 }
7143
7144 return group::dir_remove(hctx, name, id);
7145 }
7146
7147 /**
7148 * Set state of an image in the group.
7149 *
7150 * Input:
7151 * @param image_status (cls::rbd::GroupImageStatus)
7152 *
7153 * Output:
7154 * @return 0 on success, negative error code on failure
7155 */
7156 int group_image_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7157 {
7158 CLS_LOG(20, "group_image_set");
7159
7160 cls::rbd::GroupImageStatus st;
7161 try {
7162 auto iter = in->cbegin();
7163 decode(st, iter);
7164 } catch (const ceph::buffer::error &err) {
7165 return -EINVAL;
7166 }
7167
7168 string image_key = st.spec.image_key();
7169
7170 bufferlist image_val_bl;
7171 encode(st.state, image_val_bl);
7172 int r = cls_cxx_map_set_val(hctx, image_key, &image_val_bl);
7173 if (r < 0) {
7174 return r;
7175 }
7176
7177 return 0;
7178 }
7179
7180 /**
7181 * Remove reference to an image from the group.
7182 *
7183 * Input:
7184 * @param spec (cls::rbd::GroupImageSpec)
7185 *
7186 * Output:
7187 * @return 0 on success, negative error code on failure
7188 */
7189 int group_image_remove(cls_method_context_t hctx,
7190 bufferlist *in, bufferlist *out)
7191 {
7192 CLS_LOG(20, "group_image_remove");
7193 cls::rbd::GroupImageSpec spec;
7194 try {
7195 auto iter = in->cbegin();
7196 decode(spec, iter);
7197 } catch (const ceph::buffer::error &err) {
7198 return -EINVAL;
7199 }
7200
7201 string image_key = spec.image_key();
7202
7203 int r = cls_cxx_map_remove_key(hctx, image_key);
7204 if (r < 0) {
7205 CLS_ERR("error removing image from group: %s", cpp_strerror(r).c_str());
7206 return r;
7207 }
7208
7209 return 0;
7210 }
7211
7212 /*
7213 * List images in the group.
7214 *
7215 * Input:
7216 * @param start_after which name to begin listing after
7217 * (use the empty string to start at the beginning)
7218 * @param max_return the maximum number of names to list
7219 *
7220 * Output:
7221 * @param tuples of descriptions of the images: image_id, pool_id, image reference state.
7222 * @return 0 on success, negative error code on failure
7223 */
7224 int group_image_list(cls_method_context_t hctx,
7225 bufferlist *in, bufferlist *out)
7226 {
7227 CLS_LOG(20, "group_image_list");
7228 cls::rbd::GroupImageSpec start_after;
7229 uint64_t max_return;
7230 try {
7231 auto iter = in->cbegin();
7232 decode(start_after, iter);
7233 decode(max_return, iter);
7234 } catch (const ceph::buffer::error &err) {
7235 return -EINVAL;
7236 }
7237
7238 int max_read = RBD_MAX_KEYS_READ;
7239 std::map<string, bufferlist> vals;
7240 string last_read = start_after.image_key();
7241 std::vector<cls::rbd::GroupImageStatus> res;
7242 bool more;
7243 do {
7244 int r = cls_cxx_map_get_vals(hctx, last_read,
7245 cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX,
7246 max_read, &vals, &more);
7247 if (r < 0)
7248 return r;
7249
7250 for (auto it = vals.begin(); it != vals.end() && res.size() < max_return; ++it) {
7251
7252 auto iter = it->second.cbegin();
7253 cls::rbd::GroupImageLinkState state;
7254 try {
7255 decode(state, iter);
7256 } catch (const ceph::buffer::error &err) {
7257 CLS_ERR("error decoding state for image: %s", it->first.c_str());
7258 return -EIO;
7259 }
7260 cls::rbd::GroupImageSpec spec;
7261 int r = cls::rbd::GroupImageSpec::from_key(it->first, &spec);
7262 if (r < 0)
7263 return r;
7264
7265 CLS_LOG(20, "Discovered image %s %" PRId64 " %d", spec.image_id.c_str(),
7266 spec.pool_id,
7267 (int)state);
7268 res.push_back(cls::rbd::GroupImageStatus(spec, state));
7269 }
7270 if (res.size() > 0) {
7271 last_read = res.rbegin()->spec.image_key();
7272 }
7273
7274 } while (more && (res.size() < max_return));
7275 encode(res, *out);
7276
7277 return 0;
7278 }
7279
7280 /**
7281 * Reference the group this image belongs to.
7282 *
7283 * Input:
7284 * @param group_id (std::string)
7285 * @param pool_id (int64_t)
7286 *
7287 * Output:
7288 * @return 0 on success, negative error code on failure
7289 */
7290 int image_group_add(cls_method_context_t hctx,
7291 bufferlist *in, bufferlist *out)
7292 {
7293 CLS_LOG(20, "image_group_add");
7294 cls::rbd::GroupSpec new_group;
7295 try {
7296 auto iter = in->cbegin();
7297 decode(new_group, iter);
7298 } catch (const ceph::buffer::error &err) {
7299 return -EINVAL;
7300 }
7301
7302 bufferlist existing_refbl;
7303
7304 int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &existing_refbl);
7305 if (r == 0) {
7306 // If we are trying to link this image to the same group then return
7307 // success. If this image already belongs to another group then abort.
7308 cls::rbd::GroupSpec old_group;
7309 try {
7310 auto iter = existing_refbl.cbegin();
7311 decode(old_group, iter);
7312 } catch (const ceph::buffer::error &err) {
7313 return -EINVAL;
7314 }
7315
7316 if ((old_group.group_id != new_group.group_id) ||
7317 (old_group.pool_id != new_group.pool_id)) {
7318 return -EEXIST;
7319 } else {
7320 return 0; // In this case the values are already correct
7321 }
7322 } else if (r < 0 && r != -ENOENT) {
7323 // No entry means this image is not a member of any group.
7324 return r;
7325 }
7326
7327 r = image::set_op_features(hctx, RBD_OPERATION_FEATURE_GROUP,
7328 RBD_OPERATION_FEATURE_GROUP);
7329 if (r < 0) {
7330 return r;
7331 }
7332
7333 bufferlist refbl;
7334 encode(new_group, refbl);
7335 r = cls_cxx_map_set_val(hctx, RBD_GROUP_REF, &refbl);
7336 if (r < 0) {
7337 return r;
7338 }
7339
7340 return 0;
7341 }
7342
7343 /**
7344 * Remove image's pointer to the group.
7345 *
7346 * Input:
7347 * @param cg_id (std::string)
7348 * @param pool_id (int64_t)
7349 *
7350 * Output:
7351 * @return 0 on success, negative error code on failure
7352 */
7353 int image_group_remove(cls_method_context_t hctx,
7354 bufferlist *in,
7355 bufferlist *out)
7356 {
7357 CLS_LOG(20, "image_group_remove");
7358 cls::rbd::GroupSpec spec;
7359 try {
7360 auto iter = in->cbegin();
7361 decode(spec, iter);
7362 } catch (const ceph::buffer::error &err) {
7363 return -EINVAL;
7364 }
7365
7366 bufferlist refbl;
7367 int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &refbl);
7368 if (r < 0) {
7369 return r;
7370 }
7371
7372 cls::rbd::GroupSpec ref_spec;
7373 auto iter = refbl.cbegin();
7374 try {
7375 decode(ref_spec, iter);
7376 } catch (const ceph::buffer::error &err) {
7377 return -EINVAL;
7378 }
7379
7380 if (ref_spec.pool_id != spec.pool_id || ref_spec.group_id != spec.group_id) {
7381 return -EBADF;
7382 }
7383
7384 r = cls_cxx_map_remove_key(hctx, RBD_GROUP_REF);
7385 if (r < 0) {
7386 return r;
7387 }
7388
7389 r = image::set_op_features(hctx, 0, RBD_OPERATION_FEATURE_GROUP);
7390 if (r < 0) {
7391 return r;
7392 }
7393
7394 return 0;
7395 }
7396
7397 /**
7398 * Retrieve the id and pool of the group this image belongs to.
7399 *
7400 * Input:
7401 * none
7402 *
7403 * Output:
7404 * @param GroupSpec
7405 * @return 0 on success, negative error code on failure
7406 */
7407 int image_group_get(cls_method_context_t hctx,
7408 bufferlist *in, bufferlist *out)
7409 {
7410 CLS_LOG(20, "image_group_get");
7411 bufferlist refbl;
7412 int r = cls_cxx_map_get_val(hctx, RBD_GROUP_REF, &refbl);
7413 if (r < 0 && r != -ENOENT) {
7414 return r;
7415 }
7416
7417 cls::rbd::GroupSpec spec;
7418
7419 if (r != -ENOENT) {
7420 auto iter = refbl.cbegin();
7421 try {
7422 decode(spec, iter);
7423 } catch (const ceph::buffer::error &err) {
7424 return -EINVAL;
7425 }
7426 }
7427
7428 encode(spec, *out);
7429 return 0;
7430 }
7431
7432 /**
7433 * Save initial snapshot record.
7434 *
7435 * Input:
7436 * @param GroupSnapshot
7437 *
7438 * Output:
7439 * @return 0 on success, negative error code on failure
7440 */
7441 int group_snap_set(cls_method_context_t hctx,
7442 bufferlist *in, bufferlist *out)
7443 {
7444 CLS_LOG(20, "group_snap_set");
7445 cls::rbd::GroupSnapshot group_snap;
7446 try {
7447 auto iter = in->cbegin();
7448 decode(group_snap, iter);
7449 } catch (const ceph::buffer::error &err) {
7450 return -EINVAL;
7451 }
7452
7453 if (group_snap.name.empty()) {
7454 CLS_ERR("group snapshot name is empty");
7455 return -EINVAL;
7456 }
7457 if (group_snap.id.empty()) {
7458 CLS_ERR("group snapshot id is empty");
7459 return -EINVAL;
7460 }
7461
7462 int r = group::check_duplicate_snap_name(hctx, group_snap.name,
7463 group_snap.id);
7464 if (r < 0) {
7465 return r;
7466 }
7467
7468 std::string key = group::snap_key(group_snap.id);
7469 if (group_snap.state == cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE) {
7470 bufferlist snap_bl;
7471 r = cls_cxx_map_get_val(hctx, key, &snap_bl);
7472 if (r < 0 && r != -ENOENT) {
7473 return r;
7474 } else if (r >= 0) {
7475 return -EEXIST;
7476 }
7477 }
7478
7479 bufferlist obl;
7480 encode(group_snap, obl);
7481 r = cls_cxx_map_set_val(hctx, key, &obl);
7482 return r;
7483 }
7484
7485 /**
7486 * Remove snapshot record.
7487 *
7488 * Input:
7489 * @param id Snapshot id
7490 *
7491 * Output:
7492 * @return 0 on success, negative error code on failure
7493 */
7494 int group_snap_remove(cls_method_context_t hctx,
7495 bufferlist *in, bufferlist *out)
7496 {
7497 CLS_LOG(20, "group_snap_remove");
7498 std::string snap_id;
7499 try {
7500 auto iter = in->cbegin();
7501 decode(snap_id, iter);
7502 } catch (const ceph::buffer::error &err) {
7503 return -EINVAL;
7504 }
7505
7506 std::string snap_key = group::snap_key(snap_id);
7507
7508 CLS_LOG(20, "removing snapshot with key %s", snap_key.c_str());
7509 int r = cls_cxx_map_remove_key(hctx, snap_key);
7510 return r;
7511 }
7512
7513 /**
7514 * Get group's snapshot by id.
7515 *
7516 * Input:
7517 * @param snapshot_id the id of the snapshot to look for.
7518 *
7519 * Output:
7520 * @param GroupSnapshot the requested snapshot
7521 * @return 0 on success, negative error code on failure
7522 */
7523 int group_snap_get_by_id(cls_method_context_t hctx,
7524 bufferlist *in, bufferlist *out)
7525 {
7526 CLS_LOG(20, "group_snap_get_by_id");
7527
7528 std::string snap_id;
7529 try {
7530 auto iter = in->cbegin();
7531 decode(snap_id, iter);
7532 } catch (const ceph::buffer::error &err) {
7533 return -EINVAL;
7534 }
7535
7536 bufferlist snapbl;
7537
7538 int r = cls_cxx_map_get_val(hctx, group::snap_key(snap_id), &snapbl);
7539 if (r < 0) {
7540 return r;
7541 }
7542
7543 cls::rbd::GroupSnapshot group_snap;
7544 auto iter = snapbl.cbegin();
7545 try {
7546 decode(group_snap, iter);
7547 } catch (const ceph::buffer::error &err) {
7548 CLS_ERR("error decoding snapshot: %s", snap_id.c_str());
7549 return -EIO;
7550 }
7551
7552 encode(group_snap, *out);
7553
7554 return 0;
7555 }
7556
7557 /**
7558 * List group's snapshots.
7559 *
7560 * Input:
7561 * @param start_after which name to begin listing after
7562 * (use the empty string to start at the beginning)
7563 * @param max_return the maximum number of snapshots to list
7564 *
7565 * Output:
7566 * @param list of snapshots
7567 * @return 0 on success, negative error code on failure
7568 */
7569 int group_snap_list(cls_method_context_t hctx,
7570 bufferlist *in, bufferlist *out)
7571 {
7572 CLS_LOG(20, "group_snap_list");
7573
7574 cls::rbd::GroupSnapshot start_after;
7575 uint64_t max_return;
7576 try {
7577 auto iter = in->cbegin();
7578 decode(start_after, iter);
7579 decode(max_return, iter);
7580 } catch (const ceph::buffer::error &err) {
7581 return -EINVAL;
7582 }
7583 std::vector<cls::rbd::GroupSnapshot> group_snaps;
7584 group::snap_list(hctx, start_after, max_return, &group_snaps);
7585
7586 encode(group_snaps, *out);
7587
7588 return 0;
7589 }
7590
7591 namespace trash {
7592
7593 static const std::string IMAGE_KEY_PREFIX("id_");
7594
7595 std::string image_key(const std::string &image_id) {
7596 return IMAGE_KEY_PREFIX + image_id;
7597 }
7598
7599 std::string image_id_from_key(const std::string &key) {
7600 return key.substr(IMAGE_KEY_PREFIX.size());
7601 }
7602
7603 } // namespace trash
7604
7605 /**
7606 * Add an image entry to the rbd trash. Creates the trash object if
7607 * needed, and stores the trash spec information of the deleted image.
7608 *
7609 * Input:
7610 * @param id the id of the image
7611 * @param trash_spec the spec info of the deleted image
7612 *
7613 * Output:
7614 * @returns -EEXIST if the image id is already in the trash
7615 * @returns 0 on success, negative error code on failure
7616 */
7617 int trash_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7618 {
7619 int r = cls_cxx_create(hctx, false);
7620 if (r < 0) {
7621 CLS_ERR("could not create trash: %s", cpp_strerror(r).c_str());
7622 return r;
7623 }
7624
7625 string id;
7626 cls::rbd::TrashImageSpec trash_spec;
7627 try {
7628 auto iter = in->cbegin();
7629 decode(id, iter);
7630 decode(trash_spec, iter);
7631 } catch (const ceph::buffer::error &err) {
7632 return -EINVAL;
7633 }
7634
7635 if (!is_valid_id(id)) {
7636 CLS_ERR("trash_add: invalid id '%s'", id.c_str());
7637 return -EINVAL;
7638 }
7639
7640 CLS_LOG(20, "trash_add id=%s", id.c_str());
7641
7642 string key = trash::image_key(id);
7643 cls::rbd::TrashImageSpec tmp;
7644 r = read_key(hctx, key, &tmp);
7645 if (r < 0 && r != -ENOENT) {
7646 CLS_ERR("could not read key %s entry from trash: %s", key.c_str(),
7647 cpp_strerror(r).c_str());
7648 return r;
7649 } else if (r == 0) {
7650 CLS_LOG(10, "id already exists");
7651 return -EEXIST;
7652 }
7653
7654 map<string, bufferlist> omap_vals;
7655 encode(trash_spec, omap_vals[key]);
7656 return cls_cxx_map_set_vals(hctx, &omap_vals);
7657 }
7658
7659 /**
7660 * Removes an image entry from the rbd trash object.
7661 * image.
7662 *
7663 * Input:
7664 * @param id the id of the image
7665 *
7666 * Output:
7667 * @returns -ENOENT if the image id does not exist in the trash
7668 * @returns 0 on success, negative error code on failure
7669 */
7670 int trash_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7671 {
7672 string id;
7673 try {
7674 auto iter = in->cbegin();
7675 decode(id, iter);
7676 } catch (const ceph::buffer::error &err) {
7677 return -EINVAL;
7678 }
7679
7680 CLS_LOG(20, "trash_remove id=%s", id.c_str());
7681
7682 string key = trash::image_key(id);
7683 bufferlist tmp;
7684 int r = cls_cxx_map_get_val(hctx, key, &tmp);
7685 if (r < 0) {
7686 if (r != -ENOENT) {
7687 CLS_ERR("error reading entry key %s: %s", key.c_str(), cpp_strerror(r).c_str());
7688 }
7689 return r;
7690 }
7691
7692 r = cls_cxx_map_remove_key(hctx, key);
7693 if (r < 0) {
7694 CLS_ERR("error removing entry: %s", cpp_strerror(r).c_str());
7695 return r;
7696 }
7697
7698 return 0;
7699 }
7700
7701 /**
7702 * Returns the list of trash spec entries registered in the rbd_trash
7703 * object.
7704 *
7705 * Input:
7706 * @param start_after which name to begin listing after
7707 * (use the empty string to start at the beginning)
7708 * @param max_return the maximum number of names to list
7709 *
7710 * Output:
7711 * @param data the map between image id and trash spec info
7712 *
7713 * @returns 0 on success, negative error code on failure
7714 */
7715 int trash_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7716 {
7717 string start_after;
7718 uint64_t max_return;
7719
7720 try {
7721 auto iter = in->cbegin();
7722 decode(start_after, iter);
7723 decode(max_return, iter);
7724 } catch (const ceph::buffer::error &err) {
7725 return -EINVAL;
7726 }
7727
7728 map<string, cls::rbd::TrashImageSpec> data;
7729 string last_read = trash::image_key(start_after);
7730 bool more = true;
7731
7732 CLS_LOG(20, "trash_get_images");
7733 while (data.size() < max_return) {
7734 map<string, bufferlist> raw_data;
7735 int max_read = std::min<int32_t>(RBD_MAX_KEYS_READ,
7736 max_return - data.size());
7737 int r = cls_cxx_map_get_vals(hctx, last_read, trash::IMAGE_KEY_PREFIX,
7738 max_read, &raw_data, &more);
7739 if (r < 0) {
7740 if (r != -ENOENT) {
7741 CLS_ERR("failed to read the vals off of disk: %s",
7742 cpp_strerror(r).c_str());
7743 }
7744 return r;
7745 }
7746 if (raw_data.empty()) {
7747 break;
7748 }
7749
7750 for (auto it = raw_data.begin(); it != raw_data.end(); ++it) {
7751 decode(data[trash::image_id_from_key(it->first)], it->second);
7752 }
7753
7754 if (!more) {
7755 break;
7756 }
7757
7758 last_read = raw_data.rbegin()->first;
7759 }
7760
7761 encode(data, *out);
7762 return 0;
7763 }
7764
7765 /**
7766 * Returns the trash spec entry of an image registered in the rbd_trash
7767 * object.
7768 *
7769 * Input:
7770 * @param id the id of the image
7771 *
7772 * Output:
7773 * @param out the trash spec entry
7774 *
7775 * @returns 0 on success, negative error code on failure
7776 */
7777 int trash_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7778 {
7779 string id;
7780 try {
7781 auto iter = in->cbegin();
7782 decode(id, iter);
7783 } catch (const ceph::buffer::error &err) {
7784 return -EINVAL;
7785 }
7786
7787 CLS_LOG(20, "trash_get_image id=%s", id.c_str());
7788
7789
7790 string key = trash::image_key(id);
7791 bufferlist bl;
7792 int r = cls_cxx_map_get_val(hctx, key, out);
7793 if (r < 0 && r != -ENOENT) {
7794 CLS_ERR("error reading image from trash '%s': '%s'", id.c_str(),
7795 cpp_strerror(r).c_str());
7796 }
7797 return r;
7798 }
7799
7800 /**
7801 * Set state of an image in the rbd_trash object.
7802 *
7803 * Input:
7804 * @param id the id of the image
7805 * @param trash_state the state of the image to be set
7806 * @param expect_state the expected state of the image
7807 *
7808 * Output:
7809 * @returns 0 on success, negative error code on failure
7810 */
7811 int trash_state_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7812 {
7813 string id;
7814 cls::rbd::TrashImageState trash_state;
7815 cls::rbd::TrashImageState expect_state;
7816 try {
7817 auto iter = in->cbegin();
7818 decode(id, iter);
7819 decode(trash_state, iter);
7820 decode(expect_state, iter);
7821 } catch (const ceph::buffer::error &err) {
7822 return -EINVAL;
7823 }
7824
7825 CLS_LOG(20, "trash_state_set id=%s", id.c_str());
7826
7827 string key = trash::image_key(id);
7828 cls::rbd::TrashImageSpec trash_spec;
7829 int r = read_key(hctx, key, &trash_spec);
7830 if (r < 0) {
7831 if (r != -ENOENT) {
7832 CLS_ERR("Could not read trash image spec off disk: %s",
7833 cpp_strerror(r).c_str());
7834 }
7835 return r;
7836 }
7837
7838 if (trash_spec.state == expect_state) {
7839 trash_spec.state = trash_state;
7840 r = write_key(hctx, key, trash_spec);
7841 if (r < 0) {
7842 CLS_ERR("error setting trash image state: %s", cpp_strerror(r).c_str());
7843 return r;
7844 }
7845
7846 return 0;
7847 } else if (trash_spec.state == trash_state) {
7848 return 0;
7849 } else {
7850 CLS_ERR("Current trash state: %d do not match expected: %d or set: %d",
7851 trash_spec.state, expect_state, trash_state);
7852 return -ESTALE;
7853 }
7854 }
7855
7856 namespace nspace {
7857
7858 const std::string NAME_KEY_PREFIX("name_");
7859
7860 std::string key_for_name(const std::string& name) {
7861 return NAME_KEY_PREFIX + name;
7862 }
7863
7864 std::string name_from_key(const std::string &key) {
7865 return key.substr(NAME_KEY_PREFIX.size());
7866 }
7867
7868 } // namespace nspace
7869
7870 /**
7871 * Add a namespace to the namespace directory.
7872 *
7873 * Input:
7874 * @param name the name of the namespace
7875 *
7876 * Output:
7877 * @returns -EEXIST if the namespace is already exists
7878 * @returns 0 on success, negative error code on failure
7879 */
7880 int namespace_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7881 {
7882 std::string name;
7883 try {
7884 auto iter = in->cbegin();
7885 decode(name, iter);
7886 } catch (const ceph::buffer::error &err) {
7887 return -EINVAL;
7888 }
7889
7890 std::string key(nspace::key_for_name(name));
7891 bufferlist value;
7892 int r = cls_cxx_map_get_val(hctx, key, &value);
7893 if (r < 0 && r != -ENOENT) {
7894 return r;
7895 } else if (r == 0) {
7896 return -EEXIST;
7897 }
7898
7899 r = cls_cxx_map_set_val(hctx, key, &value);
7900 if (r < 0) {
7901 CLS_ERR("failed to set omap key: %s", key.c_str());
7902 return r;
7903 }
7904
7905 return 0;
7906 }
7907
7908 /**
7909 * Remove a namespace from the namespace directory.
7910 *
7911 * Input:
7912 * @param name the name of the namespace
7913 *
7914 * Output:
7915 * @returns 0 on success, negative error code on failure
7916 */
7917 int namespace_remove(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7918 {
7919 std::string name;
7920 try {
7921 auto iter = in->cbegin();
7922 decode(name, iter);
7923 } catch (const ceph::buffer::error &err) {
7924 return -EINVAL;
7925 }
7926
7927 std::string key(nspace::key_for_name(name));
7928 bufferlist bl;
7929 int r = cls_cxx_map_get_val(hctx, key, &bl);
7930 if (r < 0) {
7931 return r;
7932 }
7933
7934 r = cls_cxx_map_remove_key(hctx, key);
7935 if (r < 0) {
7936 return r;
7937 }
7938
7939 return 0;
7940 }
7941
7942 /**
7943 * Returns the list of namespaces in the rbd_namespace object
7944 *
7945 * Input:
7946 * @param start_after which name to begin listing after
7947 * (use the empty string to start at the beginning)
7948 * @param max_return the maximum number of names to list
7949 *
7950 * Output:
7951 * @param data list of namespace names
7952 * @returns 0 on success, negative error code on failure
7953 */
7954 int namespace_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
7955 {
7956 string start_after;
7957 uint64_t max_return;
7958 try {
7959 auto iter = in->cbegin();
7960 decode(start_after, iter);
7961 decode(max_return, iter);
7962 } catch (const ceph::buffer::error &err) {
7963 return -EINVAL;
7964 }
7965
7966 std::list<std::string> data;
7967 std::string last_read = nspace::key_for_name(start_after);
7968 bool more = true;
7969
7970 CLS_LOG(20, "namespace_list");
7971 while (data.size() < max_return) {
7972 std::map<std::string, bufferlist> raw_data;
7973 int max_read = std::min<int32_t>(RBD_MAX_KEYS_READ,
7974 max_return - data.size());
7975 int r = cls_cxx_map_get_vals(hctx, last_read, nspace::NAME_KEY_PREFIX,
7976 max_read, &raw_data, &more);
7977 if (r < 0) {
7978 if (r != -ENOENT) {
7979 CLS_ERR("failed to read the vals off of disk: %s",
7980 cpp_strerror(r).c_str());
7981 }
7982 return r;
7983 }
7984
7985 for (auto& it : raw_data) {
7986 data.push_back(nspace::name_from_key(it.first));
7987 }
7988
7989 if (raw_data.empty() || !more) {
7990 break;
7991 }
7992
7993 last_read = raw_data.rbegin()->first;
7994 }
7995
7996 encode(data, *out);
7997 return 0;
7998 }
7999
8000 /**
8001 * Reclaim space for zeroed extents
8002 *
8003 * Input:
8004 * @param sparse_size minimal zeroed block to sparse
8005 * @param remove_empty boolean, true if the object should be removed if empty
8006 *
8007 * Output:
8008 * @returns -ENOENT if the object does not exist or has been removed
8009 * @returns 0 on success, negative error code on failure
8010 */
8011 int sparsify(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
8012 {
8013 uint64_t sparse_size;
8014 bool remove_empty;
8015 try {
8016 auto iter = in->cbegin();
8017 decode(sparse_size, iter);
8018 decode(remove_empty, iter);
8019 } catch (const ceph::buffer::error &err) {
8020 return -EINVAL;
8021 }
8022
8023 int r = check_exists(hctx);
8024 if (r < 0) {
8025 return r;
8026 }
8027
8028 bufferlist bl;
8029 r = cls_cxx_read(hctx, 0, 0, &bl);
8030 if (r < 0) {
8031 CLS_ERR("failed to read data off of disk: %s", cpp_strerror(r).c_str());
8032 return r;
8033 }
8034
8035 if (bl.is_zero()) {
8036 if (remove_empty) {
8037 CLS_LOG(20, "remove");
8038 r = cls_cxx_remove(hctx);
8039 if (r < 0) {
8040 CLS_ERR("remove failed: %s", cpp_strerror(r).c_str());
8041 return r;
8042 }
8043 } else if (bl.length() > 0) {
8044 CLS_LOG(20, "truncate");
8045 bufferlist write_bl;
8046 r = cls_cxx_replace(hctx, 0, 0, &write_bl);
8047 if (r < 0) {
8048 CLS_ERR("truncate failed: %s", cpp_strerror(r).c_str());
8049 return r;
8050 }
8051 } else {
8052 CLS_LOG(20, "skip empty");
8053 }
8054 return 0;
8055 }
8056
8057 bl.rebuild(ceph::buffer::ptr_node::create(bl.length()));
8058 size_t write_offset = 0;
8059 size_t write_length = 0;
8060 size_t offset = 0;
8061 size_t length = bl.length();
8062 const auto& ptr = bl.front();
8063 bool replace = true;
8064 while (offset < length) {
8065 if (calc_sparse_extent(ptr, sparse_size, length, &write_offset,
8066 &write_length, &offset)) {
8067 if (write_offset == 0 && write_length == length) {
8068 CLS_LOG(20, "nothing to do");
8069 return 0;
8070 }
8071 CLS_LOG(20, "write%s %" PRIu64 "~%" PRIu64, (replace ? "(replace)" : ""),
8072 write_offset, write_length);
8073 bufferlist write_bl;
8074 write_bl.push_back(ceph::buffer::ptr_node::create(ptr, write_offset,
8075 write_length));
8076 if (replace) {
8077 r = cls_cxx_replace(hctx, write_offset, write_length, &write_bl);
8078 replace = false;
8079 } else {
8080 r = cls_cxx_write(hctx, write_offset, write_length, &write_bl);
8081 }
8082 if (r < 0) {
8083 CLS_ERR("write failed: %s", cpp_strerror(r).c_str());
8084 return r;
8085 }
8086 write_offset = offset;
8087 write_length = 0;
8088 }
8089 }
8090
8091 return 0;
8092 }
8093
8094 CLS_INIT(rbd)
8095 {
8096 CLS_LOG(20, "Loaded rbd class!");
8097
8098 cls_handle_t h_class;
8099 cls_method_handle_t h_create;
8100 cls_method_handle_t h_get_features;
8101 cls_method_handle_t h_set_features;
8102 cls_method_handle_t h_get_size;
8103 cls_method_handle_t h_set_size;
8104 cls_method_handle_t h_get_parent;
8105 cls_method_handle_t h_set_parent;
8106 cls_method_handle_t h_remove_parent;
8107 cls_method_handle_t h_parent_get;
8108 cls_method_handle_t h_parent_overlap_get;
8109 cls_method_handle_t h_parent_attach;
8110 cls_method_handle_t h_parent_detach;
8111 cls_method_handle_t h_get_protection_status;
8112 cls_method_handle_t h_set_protection_status;
8113 cls_method_handle_t h_get_stripe_unit_count;
8114 cls_method_handle_t h_set_stripe_unit_count;
8115 cls_method_handle_t h_get_create_timestamp;
8116 cls_method_handle_t h_get_access_timestamp;
8117 cls_method_handle_t h_get_modify_timestamp;
8118 cls_method_handle_t h_get_flags;
8119 cls_method_handle_t h_set_flags;
8120 cls_method_handle_t h_op_features_get;
8121 cls_method_handle_t h_op_features_set;
8122 cls_method_handle_t h_add_child;
8123 cls_method_handle_t h_remove_child;
8124 cls_method_handle_t h_get_children;
8125 cls_method_handle_t h_get_snapcontext;
8126 cls_method_handle_t h_get_object_prefix;
8127 cls_method_handle_t h_get_data_pool;
8128 cls_method_handle_t h_get_snapshot_name;
8129 cls_method_handle_t h_get_snapshot_timestamp;
8130 cls_method_handle_t h_snapshot_get;
8131 cls_method_handle_t h_snapshot_add;
8132 cls_method_handle_t h_snapshot_remove;
8133 cls_method_handle_t h_snapshot_rename;
8134 cls_method_handle_t h_snapshot_trash_add;
8135 cls_method_handle_t h_get_all_features;
8136 cls_method_handle_t h_get_id;
8137 cls_method_handle_t h_set_id;
8138 cls_method_handle_t h_set_modify_timestamp;
8139 cls_method_handle_t h_set_access_timestamp;
8140 cls_method_handle_t h_dir_get_id;
8141 cls_method_handle_t h_dir_get_name;
8142 cls_method_handle_t h_dir_list;
8143 cls_method_handle_t h_dir_add_image;
8144 cls_method_handle_t h_dir_remove_image;
8145 cls_method_handle_t h_dir_rename_image;
8146 cls_method_handle_t h_dir_state_assert;
8147 cls_method_handle_t h_dir_state_set;
8148 cls_method_handle_t h_object_map_load;
8149 cls_method_handle_t h_object_map_save;
8150 cls_method_handle_t h_object_map_resize;
8151 cls_method_handle_t h_object_map_update;
8152 cls_method_handle_t h_object_map_snap_add;
8153 cls_method_handle_t h_object_map_snap_remove;
8154 cls_method_handle_t h_metadata_set;
8155 cls_method_handle_t h_metadata_remove;
8156 cls_method_handle_t h_metadata_list;
8157 cls_method_handle_t h_metadata_get;
8158 cls_method_handle_t h_snapshot_get_limit;
8159 cls_method_handle_t h_snapshot_set_limit;
8160 cls_method_handle_t h_child_attach;
8161 cls_method_handle_t h_child_detach;
8162 cls_method_handle_t h_children_list;
8163 cls_method_handle_t h_migration_set;
8164 cls_method_handle_t h_migration_set_state;
8165 cls_method_handle_t h_migration_get;
8166 cls_method_handle_t h_migration_remove;
8167 cls_method_handle_t h_old_snapshots_list;
8168 cls_method_handle_t h_old_snapshot_add;
8169 cls_method_handle_t h_old_snapshot_remove;
8170 cls_method_handle_t h_old_snapshot_rename;
8171 cls_method_handle_t h_mirror_uuid_get;
8172 cls_method_handle_t h_mirror_uuid_set;
8173 cls_method_handle_t h_mirror_mode_get;
8174 cls_method_handle_t h_mirror_mode_set;
8175 cls_method_handle_t h_mirror_peer_ping;
8176 cls_method_handle_t h_mirror_peer_list;
8177 cls_method_handle_t h_mirror_peer_add;
8178 cls_method_handle_t h_mirror_peer_remove;
8179 cls_method_handle_t h_mirror_peer_set_client;
8180 cls_method_handle_t h_mirror_peer_set_cluster;
8181 cls_method_handle_t h_mirror_peer_set_direction;
8182 cls_method_handle_t h_mirror_image_list;
8183 cls_method_handle_t h_mirror_image_get_image_id;
8184 cls_method_handle_t h_mirror_image_get;
8185 cls_method_handle_t h_mirror_image_set;
8186 cls_method_handle_t h_mirror_image_remove;
8187 cls_method_handle_t h_mirror_image_status_set;
8188 cls_method_handle_t h_mirror_image_status_remove;
8189 cls_method_handle_t h_mirror_image_status_get;
8190 cls_method_handle_t h_mirror_image_status_list;
8191 cls_method_handle_t h_mirror_image_status_get_summary;
8192 cls_method_handle_t h_mirror_image_status_remove_down;
8193 cls_method_handle_t h_mirror_image_instance_get;
8194 cls_method_handle_t h_mirror_image_instance_list;
8195 cls_method_handle_t h_mirror_instances_list;
8196 cls_method_handle_t h_mirror_instances_add;
8197 cls_method_handle_t h_mirror_instances_remove;
8198 cls_method_handle_t h_mirror_image_map_list;
8199 cls_method_handle_t h_mirror_image_map_update;
8200 cls_method_handle_t h_mirror_image_map_remove;
8201 cls_method_handle_t h_mirror_image_snapshot_unlink_peer;
8202 cls_method_handle_t h_mirror_image_snapshot_set_copy_progress;
8203 cls_method_handle_t h_group_dir_list;
8204 cls_method_handle_t h_group_dir_add;
8205 cls_method_handle_t h_group_dir_remove;
8206 cls_method_handle_t h_group_dir_rename;
8207 cls_method_handle_t h_group_image_remove;
8208 cls_method_handle_t h_group_image_list;
8209 cls_method_handle_t h_group_image_set;
8210 cls_method_handle_t h_image_group_add;
8211 cls_method_handle_t h_image_group_remove;
8212 cls_method_handle_t h_image_group_get;
8213 cls_method_handle_t h_group_snap_set;
8214 cls_method_handle_t h_group_snap_remove;
8215 cls_method_handle_t h_group_snap_get_by_id;
8216 cls_method_handle_t h_group_snap_list;
8217 cls_method_handle_t h_trash_add;
8218 cls_method_handle_t h_trash_remove;
8219 cls_method_handle_t h_trash_list;
8220 cls_method_handle_t h_trash_get;
8221 cls_method_handle_t h_trash_state_set;
8222 cls_method_handle_t h_namespace_add;
8223 cls_method_handle_t h_namespace_remove;
8224 cls_method_handle_t h_namespace_list;
8225 cls_method_handle_t h_copyup;
8226 cls_method_handle_t h_sparse_copyup;
8227 cls_method_handle_t h_assert_snapc_seq;
8228 cls_method_handle_t h_sparsify;
8229
8230 cls_register("rbd", &h_class);
8231 cls_register_cxx_method(h_class, "create",
8232 CLS_METHOD_RD | CLS_METHOD_WR,
8233 create, &h_create);
8234 cls_register_cxx_method(h_class, "get_features",
8235 CLS_METHOD_RD,
8236 get_features, &h_get_features);
8237 cls_register_cxx_method(h_class, "set_features",
8238 CLS_METHOD_RD | CLS_METHOD_WR,
8239 set_features, &h_set_features);
8240 cls_register_cxx_method(h_class, "get_size",
8241 CLS_METHOD_RD,
8242 get_size, &h_get_size);
8243 cls_register_cxx_method(h_class, "set_size",
8244 CLS_METHOD_RD | CLS_METHOD_WR,
8245 set_size, &h_set_size);
8246 cls_register_cxx_method(h_class, "get_snapcontext",
8247 CLS_METHOD_RD,
8248 get_snapcontext, &h_get_snapcontext);
8249 cls_register_cxx_method(h_class, "get_object_prefix",
8250 CLS_METHOD_RD,
8251 get_object_prefix, &h_get_object_prefix);
8252 cls_register_cxx_method(h_class, "get_data_pool", CLS_METHOD_RD,
8253 get_data_pool, &h_get_data_pool);
8254 cls_register_cxx_method(h_class, "get_snapshot_name",
8255 CLS_METHOD_RD,
8256 get_snapshot_name, &h_get_snapshot_name);
8257 cls_register_cxx_method(h_class, "get_snapshot_timestamp",
8258 CLS_METHOD_RD,
8259 get_snapshot_timestamp, &h_get_snapshot_timestamp);
8260 cls_register_cxx_method(h_class, "snapshot_get",
8261 CLS_METHOD_RD,
8262 snapshot_get, &h_snapshot_get);
8263 cls_register_cxx_method(h_class, "snapshot_add",
8264 CLS_METHOD_RD | CLS_METHOD_WR,
8265 snapshot_add, &h_snapshot_add);
8266 cls_register_cxx_method(h_class, "snapshot_remove",
8267 CLS_METHOD_RD | CLS_METHOD_WR,
8268 snapshot_remove, &h_snapshot_remove);
8269 cls_register_cxx_method(h_class, "snapshot_rename",
8270 CLS_METHOD_RD | CLS_METHOD_WR,
8271 snapshot_rename, &h_snapshot_rename);
8272 cls_register_cxx_method(h_class, "snapshot_trash_add",
8273 CLS_METHOD_RD | CLS_METHOD_WR,
8274 snapshot_trash_add, &h_snapshot_trash_add);
8275 cls_register_cxx_method(h_class, "get_all_features",
8276 CLS_METHOD_RD,
8277 get_all_features, &h_get_all_features);
8278
8279 // NOTE: deprecate v1 parent APIs after mimic EOLed
8280 cls_register_cxx_method(h_class, "get_parent",
8281 CLS_METHOD_RD,
8282 get_parent, &h_get_parent);
8283 cls_register_cxx_method(h_class, "set_parent",
8284 CLS_METHOD_RD | CLS_METHOD_WR,
8285 set_parent, &h_set_parent);
8286 cls_register_cxx_method(h_class, "remove_parent",
8287 CLS_METHOD_RD | CLS_METHOD_WR,
8288 remove_parent, &h_remove_parent);
8289
8290 cls_register_cxx_method(h_class, "parent_get",
8291 CLS_METHOD_RD, parent_get, &h_parent_get);
8292 cls_register_cxx_method(h_class, "parent_overlap_get",
8293 CLS_METHOD_RD, parent_overlap_get,
8294 &h_parent_overlap_get);
8295 cls_register_cxx_method(h_class, "parent_attach",
8296 CLS_METHOD_RD | CLS_METHOD_WR,
8297 parent_attach, &h_parent_attach);
8298 cls_register_cxx_method(h_class, "parent_detach",
8299 CLS_METHOD_RD | CLS_METHOD_WR,
8300 parent_detach, &h_parent_detach);
8301
8302 cls_register_cxx_method(h_class, "set_protection_status",
8303 CLS_METHOD_RD | CLS_METHOD_WR,
8304 set_protection_status, &h_set_protection_status);
8305 cls_register_cxx_method(h_class, "get_protection_status",
8306 CLS_METHOD_RD,
8307 get_protection_status, &h_get_protection_status);
8308 cls_register_cxx_method(h_class, "get_stripe_unit_count",
8309 CLS_METHOD_RD,
8310 get_stripe_unit_count, &h_get_stripe_unit_count);
8311 cls_register_cxx_method(h_class, "set_stripe_unit_count",
8312 CLS_METHOD_RD | CLS_METHOD_WR,
8313 set_stripe_unit_count, &h_set_stripe_unit_count);
8314 cls_register_cxx_method(h_class, "get_create_timestamp",
8315 CLS_METHOD_RD,
8316 get_create_timestamp, &h_get_create_timestamp);
8317 cls_register_cxx_method(h_class, "get_access_timestamp",
8318 CLS_METHOD_RD,
8319 get_access_timestamp, &h_get_access_timestamp);
8320 cls_register_cxx_method(h_class, "get_modify_timestamp",
8321 CLS_METHOD_RD,
8322 get_modify_timestamp, &h_get_modify_timestamp);
8323 cls_register_cxx_method(h_class, "get_flags",
8324 CLS_METHOD_RD,
8325 get_flags, &h_get_flags);
8326 cls_register_cxx_method(h_class, "set_flags",
8327 CLS_METHOD_RD | CLS_METHOD_WR,
8328 set_flags, &h_set_flags);
8329 cls_register_cxx_method(h_class, "op_features_get", CLS_METHOD_RD,
8330 op_features_get, &h_op_features_get);
8331 cls_register_cxx_method(h_class, "op_features_set",
8332 CLS_METHOD_RD | CLS_METHOD_WR,
8333 op_features_set, &h_op_features_set);
8334 cls_register_cxx_method(h_class, "metadata_list",
8335 CLS_METHOD_RD,
8336 metadata_list, &h_metadata_list);
8337 cls_register_cxx_method(h_class, "metadata_set",
8338 CLS_METHOD_RD | CLS_METHOD_WR,
8339 metadata_set, &h_metadata_set);
8340 cls_register_cxx_method(h_class, "metadata_remove",
8341 CLS_METHOD_RD | CLS_METHOD_WR,
8342 metadata_remove, &h_metadata_remove);
8343 cls_register_cxx_method(h_class, "metadata_get",
8344 CLS_METHOD_RD,
8345 metadata_get, &h_metadata_get);
8346 cls_register_cxx_method(h_class, "snapshot_get_limit",
8347 CLS_METHOD_RD,
8348 snapshot_get_limit, &h_snapshot_get_limit);
8349 cls_register_cxx_method(h_class, "snapshot_set_limit",
8350 CLS_METHOD_RD | CLS_METHOD_WR,
8351 snapshot_set_limit, &h_snapshot_set_limit);
8352 cls_register_cxx_method(h_class, "child_attach",
8353 CLS_METHOD_RD | CLS_METHOD_WR,
8354 child_attach, &h_child_attach);
8355 cls_register_cxx_method(h_class, "child_detach",
8356 CLS_METHOD_RD | CLS_METHOD_WR,
8357 child_detach, &h_child_detach);
8358 cls_register_cxx_method(h_class, "children_list",
8359 CLS_METHOD_RD,
8360 children_list, &h_children_list);
8361 cls_register_cxx_method(h_class, "migration_set",
8362 CLS_METHOD_RD | CLS_METHOD_WR,
8363 migration_set, &h_migration_set);
8364 cls_register_cxx_method(h_class, "migration_set_state",
8365 CLS_METHOD_RD | CLS_METHOD_WR,
8366 migration_set_state, &h_migration_set_state);
8367 cls_register_cxx_method(h_class, "migration_get",
8368 CLS_METHOD_RD,
8369 migration_get, &h_migration_get);
8370 cls_register_cxx_method(h_class, "migration_remove",
8371 CLS_METHOD_RD | CLS_METHOD_WR,
8372 migration_remove, &h_migration_remove);
8373
8374 cls_register_cxx_method(h_class, "set_modify_timestamp",
8375 CLS_METHOD_RD | CLS_METHOD_WR,
8376 set_modify_timestamp, &h_set_modify_timestamp);
8377
8378 cls_register_cxx_method(h_class, "set_access_timestamp",
8379 CLS_METHOD_RD | CLS_METHOD_WR,
8380 set_access_timestamp, &h_set_access_timestamp);
8381
8382 /* methods for the rbd_children object */
8383 cls_register_cxx_method(h_class, "add_child",
8384 CLS_METHOD_RD | CLS_METHOD_WR,
8385 add_child, &h_add_child);
8386 cls_register_cxx_method(h_class, "remove_child",
8387 CLS_METHOD_RD | CLS_METHOD_WR,
8388 remove_child, &h_remove_child);
8389 cls_register_cxx_method(h_class, "get_children",
8390 CLS_METHOD_RD,
8391 get_children, &h_get_children);
8392
8393 /* methods for the rbd_id.$image_name objects */
8394 cls_register_cxx_method(h_class, "get_id",
8395 CLS_METHOD_RD,
8396 get_id, &h_get_id);
8397 cls_register_cxx_method(h_class, "set_id",
8398 CLS_METHOD_RD | CLS_METHOD_WR,
8399 set_id, &h_set_id);
8400
8401 /* methods for the rbd_directory object */
8402 cls_register_cxx_method(h_class, "dir_get_id",
8403 CLS_METHOD_RD,
8404 dir_get_id, &h_dir_get_id);
8405 cls_register_cxx_method(h_class, "dir_get_name",
8406 CLS_METHOD_RD,
8407 dir_get_name, &h_dir_get_name);
8408 cls_register_cxx_method(h_class, "dir_list",
8409 CLS_METHOD_RD,
8410 dir_list, &h_dir_list);
8411 cls_register_cxx_method(h_class, "dir_add_image",
8412 CLS_METHOD_RD | CLS_METHOD_WR,
8413 dir_add_image, &h_dir_add_image);
8414 cls_register_cxx_method(h_class, "dir_remove_image",
8415 CLS_METHOD_RD | CLS_METHOD_WR,
8416 dir_remove_image, &h_dir_remove_image);
8417 cls_register_cxx_method(h_class, "dir_rename_image",
8418 CLS_METHOD_RD | CLS_METHOD_WR,
8419 dir_rename_image, &h_dir_rename_image);
8420 cls_register_cxx_method(h_class, "dir_state_assert", CLS_METHOD_RD,
8421 dir_state_assert, &h_dir_state_assert);
8422 cls_register_cxx_method(h_class, "dir_state_set",
8423 CLS_METHOD_RD | CLS_METHOD_WR,
8424 dir_state_set, &h_dir_state_set);
8425
8426 /* methods for the rbd_object_map.$image_id object */
8427 cls_register_cxx_method(h_class, "object_map_load",
8428 CLS_METHOD_RD,
8429 object_map_load, &h_object_map_load);
8430 cls_register_cxx_method(h_class, "object_map_save",
8431 CLS_METHOD_RD | CLS_METHOD_WR,
8432 object_map_save, &h_object_map_save);
8433 cls_register_cxx_method(h_class, "object_map_resize",
8434 CLS_METHOD_RD | CLS_METHOD_WR,
8435 object_map_resize, &h_object_map_resize);
8436 cls_register_cxx_method(h_class, "object_map_update",
8437 CLS_METHOD_RD | CLS_METHOD_WR,
8438 object_map_update, &h_object_map_update);
8439 cls_register_cxx_method(h_class, "object_map_snap_add",
8440 CLS_METHOD_RD | CLS_METHOD_WR,
8441 object_map_snap_add, &h_object_map_snap_add);
8442 cls_register_cxx_method(h_class, "object_map_snap_remove",
8443 CLS_METHOD_RD | CLS_METHOD_WR,
8444 object_map_snap_remove, &h_object_map_snap_remove);
8445
8446 /* methods for the old format */
8447 cls_register_cxx_method(h_class, "snap_list",
8448 CLS_METHOD_RD,
8449 old_snapshots_list, &h_old_snapshots_list);
8450 cls_register_cxx_method(h_class, "snap_add",
8451 CLS_METHOD_RD | CLS_METHOD_WR,
8452 old_snapshot_add, &h_old_snapshot_add);
8453 cls_register_cxx_method(h_class, "snap_remove",
8454 CLS_METHOD_RD | CLS_METHOD_WR,
8455 old_snapshot_remove, &h_old_snapshot_remove);
8456 cls_register_cxx_method(h_class, "snap_rename",
8457 CLS_METHOD_RD | CLS_METHOD_WR,
8458 old_snapshot_rename, &h_old_snapshot_rename);
8459
8460 /* methods for the rbd_mirroring object */
8461 cls_register_cxx_method(h_class, "mirror_uuid_get", CLS_METHOD_RD,
8462 mirror_uuid_get, &h_mirror_uuid_get);
8463 cls_register_cxx_method(h_class, "mirror_uuid_set",
8464 CLS_METHOD_RD | CLS_METHOD_WR,
8465 mirror_uuid_set, &h_mirror_uuid_set);
8466 cls_register_cxx_method(h_class, "mirror_mode_get", CLS_METHOD_RD,
8467 mirror_mode_get, &h_mirror_mode_get);
8468 cls_register_cxx_method(h_class, "mirror_mode_set",
8469 CLS_METHOD_RD | CLS_METHOD_WR,
8470 mirror_mode_set, &h_mirror_mode_set);
8471 cls_register_cxx_method(h_class, "mirror_peer_ping",
8472 CLS_METHOD_RD | CLS_METHOD_WR,
8473 mirror_peer_ping, &h_mirror_peer_ping);
8474 cls_register_cxx_method(h_class, "mirror_peer_list", CLS_METHOD_RD,
8475 mirror_peer_list, &h_mirror_peer_list);
8476 cls_register_cxx_method(h_class, "mirror_peer_add",
8477 CLS_METHOD_RD | CLS_METHOD_WR,
8478 mirror_peer_add, &h_mirror_peer_add);
8479 cls_register_cxx_method(h_class, "mirror_peer_remove",
8480 CLS_METHOD_RD | CLS_METHOD_WR,
8481 mirror_peer_remove, &h_mirror_peer_remove);
8482 cls_register_cxx_method(h_class, "mirror_peer_set_client",
8483 CLS_METHOD_RD | CLS_METHOD_WR,
8484 mirror_peer_set_client, &h_mirror_peer_set_client);
8485 cls_register_cxx_method(h_class, "mirror_peer_set_cluster",
8486 CLS_METHOD_RD | CLS_METHOD_WR,
8487 mirror_peer_set_cluster, &h_mirror_peer_set_cluster);
8488 cls_register_cxx_method(h_class, "mirror_peer_set_direction",
8489 CLS_METHOD_RD | CLS_METHOD_WR,
8490 mirror_peer_set_direction,
8491 &h_mirror_peer_set_direction);
8492 cls_register_cxx_method(h_class, "mirror_image_list", CLS_METHOD_RD,
8493 mirror_image_list, &h_mirror_image_list);
8494 cls_register_cxx_method(h_class, "mirror_image_get_image_id", CLS_METHOD_RD,
8495 mirror_image_get_image_id,
8496 &h_mirror_image_get_image_id);
8497 cls_register_cxx_method(h_class, "mirror_image_get", CLS_METHOD_RD,
8498 mirror_image_get, &h_mirror_image_get);
8499 cls_register_cxx_method(h_class, "mirror_image_set",
8500 CLS_METHOD_RD | CLS_METHOD_WR,
8501 mirror_image_set, &h_mirror_image_set);
8502 cls_register_cxx_method(h_class, "mirror_image_remove",
8503 CLS_METHOD_RD | CLS_METHOD_WR,
8504 mirror_image_remove, &h_mirror_image_remove);
8505 cls_register_cxx_method(h_class, "mirror_image_status_set",
8506 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
8507 mirror_image_status_set, &h_mirror_image_status_set);
8508 cls_register_cxx_method(h_class, "mirror_image_status_remove",
8509 CLS_METHOD_RD | CLS_METHOD_WR,
8510 mirror_image_status_remove,
8511 &h_mirror_image_status_remove);
8512 cls_register_cxx_method(h_class, "mirror_image_status_get", CLS_METHOD_RD,
8513 mirror_image_status_get, &h_mirror_image_status_get);
8514 cls_register_cxx_method(h_class, "mirror_image_status_list", CLS_METHOD_RD,
8515 mirror_image_status_list,
8516 &h_mirror_image_status_list);
8517 cls_register_cxx_method(h_class, "mirror_image_status_get_summary",
8518 CLS_METHOD_RD, mirror_image_status_get_summary,
8519 &h_mirror_image_status_get_summary);
8520 cls_register_cxx_method(h_class, "mirror_image_status_remove_down",
8521 CLS_METHOD_RD | CLS_METHOD_WR,
8522 mirror_image_status_remove_down,
8523 &h_mirror_image_status_remove_down);
8524 cls_register_cxx_method(h_class, "mirror_image_instance_get", CLS_METHOD_RD,
8525 mirror_image_instance_get,
8526 &h_mirror_image_instance_get);
8527 cls_register_cxx_method(h_class, "mirror_image_instance_list", CLS_METHOD_RD,
8528 mirror_image_instance_list,
8529 &h_mirror_image_instance_list);
8530 cls_register_cxx_method(h_class, "mirror_instances_list", CLS_METHOD_RD,
8531 mirror_instances_list, &h_mirror_instances_list);
8532 cls_register_cxx_method(h_class, "mirror_instances_add",
8533 CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PROMOTE,
8534 mirror_instances_add, &h_mirror_instances_add);
8535 cls_register_cxx_method(h_class, "mirror_instances_remove",
8536 CLS_METHOD_RD | CLS_METHOD_WR,
8537 mirror_instances_remove,
8538 &h_mirror_instances_remove);
8539 cls_register_cxx_method(h_class, "mirror_image_map_list",
8540 CLS_METHOD_RD, mirror_image_map_list,
8541 &h_mirror_image_map_list);
8542 cls_register_cxx_method(h_class, "mirror_image_map_update",
8543 CLS_METHOD_WR, mirror_image_map_update,
8544 &h_mirror_image_map_update);
8545 cls_register_cxx_method(h_class, "mirror_image_map_remove",
8546 CLS_METHOD_WR, mirror_image_map_remove,
8547 &h_mirror_image_map_remove);
8548 cls_register_cxx_method(h_class, "mirror_image_snapshot_unlink_peer",
8549 CLS_METHOD_RD | CLS_METHOD_WR,
8550 mirror_image_snapshot_unlink_peer,
8551 &h_mirror_image_snapshot_unlink_peer);
8552 cls_register_cxx_method(h_class, "mirror_image_snapshot_set_copy_progress",
8553 CLS_METHOD_RD | CLS_METHOD_WR,
8554 mirror_image_snapshot_set_copy_progress,
8555 &h_mirror_image_snapshot_set_copy_progress);
8556
8557 /* methods for the groups feature */
8558 cls_register_cxx_method(h_class, "group_dir_list",
8559 CLS_METHOD_RD,
8560 group_dir_list, &h_group_dir_list);
8561 cls_register_cxx_method(h_class, "group_dir_add",
8562 CLS_METHOD_RD | CLS_METHOD_WR,
8563 group_dir_add, &h_group_dir_add);
8564 cls_register_cxx_method(h_class, "group_dir_remove",
8565 CLS_METHOD_RD | CLS_METHOD_WR,
8566 group_dir_remove, &h_group_dir_remove);
8567 cls_register_cxx_method(h_class, "group_dir_rename",
8568 CLS_METHOD_RD | CLS_METHOD_WR,
8569 group_dir_rename, &h_group_dir_rename);
8570 cls_register_cxx_method(h_class, "group_image_remove",
8571 CLS_METHOD_RD | CLS_METHOD_WR,
8572 group_image_remove, &h_group_image_remove);
8573 cls_register_cxx_method(h_class, "group_image_list",
8574 CLS_METHOD_RD,
8575 group_image_list, &h_group_image_list);
8576 cls_register_cxx_method(h_class, "group_image_set",
8577 CLS_METHOD_RD | CLS_METHOD_WR,
8578 group_image_set, &h_group_image_set);
8579 cls_register_cxx_method(h_class, "image_group_add",
8580 CLS_METHOD_RD | CLS_METHOD_WR,
8581 image_group_add, &h_image_group_add);
8582 cls_register_cxx_method(h_class, "image_group_remove",
8583 CLS_METHOD_RD | CLS_METHOD_WR,
8584 image_group_remove, &h_image_group_remove);
8585 cls_register_cxx_method(h_class, "image_group_get",
8586 CLS_METHOD_RD,
8587 image_group_get, &h_image_group_get);
8588 cls_register_cxx_method(h_class, "group_snap_set",
8589 CLS_METHOD_RD | CLS_METHOD_WR,
8590 group_snap_set, &h_group_snap_set);
8591 cls_register_cxx_method(h_class, "group_snap_remove",
8592 CLS_METHOD_RD | CLS_METHOD_WR,
8593 group_snap_remove, &h_group_snap_remove);
8594 cls_register_cxx_method(h_class, "group_snap_get_by_id",
8595 CLS_METHOD_RD,
8596 group_snap_get_by_id, &h_group_snap_get_by_id);
8597 cls_register_cxx_method(h_class, "group_snap_list",
8598 CLS_METHOD_RD,
8599 group_snap_list, &h_group_snap_list);
8600
8601 /* rbd_trash object methods */
8602 cls_register_cxx_method(h_class, "trash_add",
8603 CLS_METHOD_RD | CLS_METHOD_WR,
8604 trash_add, &h_trash_add);
8605 cls_register_cxx_method(h_class, "trash_remove",
8606 CLS_METHOD_RD | CLS_METHOD_WR,
8607 trash_remove, &h_trash_remove);
8608 cls_register_cxx_method(h_class, "trash_list",
8609 CLS_METHOD_RD,
8610 trash_list, &h_trash_list);
8611 cls_register_cxx_method(h_class, "trash_get",
8612 CLS_METHOD_RD,
8613 trash_get, &h_trash_get);
8614 cls_register_cxx_method(h_class, "trash_state_set",
8615 CLS_METHOD_RD | CLS_METHOD_WR,
8616 trash_state_set, &h_trash_state_set);
8617
8618 /* rbd_namespace object methods */
8619 cls_register_cxx_method(h_class, "namespace_add",
8620 CLS_METHOD_RD | CLS_METHOD_WR,
8621 namespace_add, &h_namespace_add);
8622 cls_register_cxx_method(h_class, "namespace_remove",
8623 CLS_METHOD_RD | CLS_METHOD_WR,
8624 namespace_remove, &h_namespace_remove);
8625 cls_register_cxx_method(h_class, "namespace_list", CLS_METHOD_RD,
8626 namespace_list, &h_namespace_list);
8627
8628 /* data object methods */
8629 cls_register_cxx_method(h_class, "copyup",
8630 CLS_METHOD_RD | CLS_METHOD_WR,
8631 copyup, &h_copyup);
8632 cls_register_cxx_method(h_class, "sparse_copyup",
8633 CLS_METHOD_RD | CLS_METHOD_WR,
8634 sparse_copyup, &h_sparse_copyup);
8635 cls_register_cxx_method(h_class, "assert_snapc_seq",
8636 CLS_METHOD_RD | CLS_METHOD_WR,
8637 assert_snapc_seq,
8638 &h_assert_snapc_seq);
8639 cls_register_cxx_method(h_class, "sparsify",
8640 CLS_METHOD_RD | CLS_METHOD_WR,
8641 sparsify, &h_sparsify);
8642 }